{
  "openapi": "3.1.0",
  "info": {
    "title": "officina.it API",
    "version": "2026-09-05",
    "description": "API REST pubblica di officina.it: consente di leggere e scrivere i dati\ndella propria officina da altri programmi (gestionali contabili, sistemi\ndi flotta, strumenti di analisi).\n\nL'autenticazione avviene con una chiave API (`Authorization: Bearer\nofk_live_…`) generata da Impostazioni → Accesso API. Ogni chiave porta\npermessi per risorsa e appartiene a una singola officina.\n\nOgni richiesta deve indicare la revisione del contratto con l'header\n`X-API-Version`. Il percorso `/v1` identifica la versione maggiore, che\ncambia solo per modifiche non retrocompatibili; la revisione identifica\nla data del contratto. La documentazione completa è su\nhttps://officina.it/api.\n\nDate e orari viaggiano in RFC 3339 (UTC). Le date scritte dal client\ndevono cadere fra il 1° gennaio 1900 e il 31 dicembre 2100: fuori da\nquesto intervallo la risposta è `422 parameter_invalid` con `param` sul\ncampo. È lo stesso intervallo accettato dai selettori di data\ndell'applicazione web, così un record creato via API resta modificabile\nanche dall'interfaccia. I timestamp generati dal server (`created_at`,\n`updated_at`, …) non hanno questo vincolo.\n",
    "contact": {
      "name": "officina.it engineering",
      "email": "assistenza@officina.it"
    }
  },
  "servers": [
    {
      "url": "https://api.officina.it",
      "description": "Production"
    },
    {
      "url": "http://localhost:8084",
      "description": "Local development"
    }
  ],
  "security": [
    {
      "PublicAPIKey": []
    }
  ],
  "tags": [
    {
      "name": "Receipts",
      "description": "Scontrini e corrispettivi."
    },
    {
      "name": "Expenses",
      "description": "Spese e categorie di spesa."
    },
    {
      "name": "Suppliers",
      "description": "Fornitori, ordini e documenti di trasporto."
    },
    {
      "name": "TyreSets",
      "description": "Deposito gomme."
    },
    {
      "name": "ElectronicInvoicing",
      "description": "Fatturazione elettronica (sola lettura)."
    },
    {
      "name": "Reports",
      "description": "Report e statistiche."
    },
    {
      "name": "Dealership",
      "description": "Concessionaria: stock e vendite veicoli."
    },
    {
      "name": "Settings",
      "description": "Impostazioni aziendali, aliquote IVA, sezionali, team."
    },
    {
      "name": "Authentication",
      "description": "Endpoints that exchange credentials for a mobile bearer JWT.\nNone of these require auth — they issue it.\n"
    },
    {
      "name": "Customers",
      "description": "The workshop's clients. Multi-tenant: every endpoint is scoped to\nthe authenticated user's company. Cross-company access returns 404,\nnever 403 (don't leak the existence of other workshops' customers).\n"
    },
    {
      "name": "Vehicles",
      "description": "Vehicles owned (or managed) by workshop clients. Multi-tenant:\nevery endpoint is scoped to the authenticated user's company.\nCross-company access returns 404, never 403.\n\nRental setup (is_rental, rental_*) is managed through a\nseparate flow; those fields are read-only on this resource.\n"
    },
    {
      "name": "Articles",
      "description": "Read-only catalog for the line-item editor: articles back both\nthe magazzino (parts) and the listino manodopera (labor), plus\nthe company VAT rates its IVA picker offers. Multi-tenant:\nscoped to the authenticated user's company.\n"
    },
    {
      "name": "Worksheets",
      "description": "Work orders / job cards. Multi-tenant: every endpoint is scoped\nto the authenticated user's company. Cross-company access\nreturns 404, never 403. Status changes go through the\n`/v1/worksheets/{id}/transitions` RPC instead of PATCH so the\nlifecycle is explicit in the URL surface.\n"
    },
    {
      "name": "Quotes",
      "description": "Customer-facing job estimates. Multi-tenant: every endpoint is\nscoped to the authenticated user's company. Status flows\nwaiting → accepted / rejected through the\n`/v1/quotes/{id}/transitions` RPC.\n"
    },
    {
      "name": "Invoices",
      "description": "Workshop invoices. Multi-tenant: every endpoint is scoped to the\nauthenticated user's company. Cross-company access returns 404,\nnever 403. Invoices flow through a draft → issued lifecycle;\nonce issued, the `seq_number` is assigned atomically per-company\nand `issued_at` is stamped. Issued invoices remain patchable for\na limited subset of fields (the service layer is the source of\ntruth on what's mutable post-issuance).\n"
    },
    {
      "name": "CreditNotes",
      "description": "Credit notes are derivative documents — each one is created\nFROM an existing invoice. Multi-tenant: every endpoint is\nscoped to the authenticated user's company. Cross-company\naccess returns 404, never 403. Deleting a credit note with an\nalready-transmitted SDI electronic counterpart returns 409.\n"
    },
    {
      "name": "Checkins",
      "description": "Vehicle intake / inspection snapshots. Each checkin captures the\nvehicle's state on arrival (fuel level, kilometres, free-form\nsection notes) and progresses through an `active` → `completed`\nlifecycle. Multi-tenant: every endpoint is scoped to the\nauthenticated user's company. Cross-company access returns 404.\n\nStatus transitions live behind their own verb\n(`POST /v1/checkins/{id}/transitions`) so the server can stamp\nlifecycle timestamps and run side effects centrally — flipping\n`status` through PATCH is intentionally not supported. Line-item\nmutation is out of scope for v1 PATCH and will land as a dedicated\nsub-resource later.\n"
    },
    {
      "name": "Calendar",
      "description": "Workshop appointments and reminders. Each entry belongs to\nexactly one company (the caller's) and is owned by the user\nwho created it. Entries may reference an optional Customer,\nVehicle, work category, and parent Calendar. The parent\nCalendar container is not exposed on this tier — only entries.\n"
    },
    {
      "name": "CashBook",
      "description": "Cash-flow ledger derived from payments. Each entry is a row in\nthe workshop's cashbook: an income (from an invoice or worksheet)\nor an expense (from a supplier bill), carrying a cumulative\n`running_balance`. Read-only — the cashbook is a projection, not\na first-class resource; to mutate it, create or delete the\nunderlying payment/expense. Multi-tenant: every entry is scoped\nto the authenticated user's company.\n"
    }
  ],
  "x-doc-groups": [
    {
      "slug": "clienti",
      "name": "Clienti",
      "tags": [
        "Customers"
      ],
      "ready": true,
      "description": "Anagrafiche, documenti allegati e storico."
    },
    {
      "slug": "veicoli",
      "name": "Veicoli",
      "tags": [
        "Vehicles"
      ],
      "ready": true,
      "description": "Veicoli, marche e documenti allegati."
    },
    {
      "slug": "schede-lavoro",
      "name": "Schede lavoro",
      "tags": [
        "Worksheets"
      ],
      "ready": true,
      "description": "Schede, righe, pagamenti e stati di lavorazione."
    },
    {
      "slug": "preventivi",
      "name": "Preventivi",
      "tags": [
        "Quotes"
      ],
      "ready": true,
      "description": "Preventivi, righe e conversione in scheda lavoro."
    },
    {
      "slug": "fatture",
      "name": "Fatture",
      "tags": [
        "Invoices",
        "CreditNotes"
      ],
      "ready": true,
      "description": "Fatture, note di credito, pagamenti ed emissione."
    },
    {
      "slug": "scontrini",
      "name": "Scontrini",
      "tags": [
        "Receipts"
      ],
      "ready": true,
      "description": "Scontrini e relativi pagamenti."
    },
    {
      "slug": "accettazioni",
      "name": "Accettazioni",
      "tags": [
        "Checkins"
      ],
      "ready": true,
      "description": "Accettazioni, conversioni e documenti."
    },
    {
      "slug": "agenda",
      "name": "Agenda",
      "tags": [
        "Calendar"
      ],
      "ready": true,
      "description": "Calendari e appuntamenti."
    },
    {
      "slug": "magazzino",
      "name": "Magazzino",
      "tags": [
        "Articles"
      ],
      "ready": true,
      "description": "Articoli, listino manodopera e movimenti."
    },
    {
      "slug": "deposito-gomme",
      "name": "Deposito gomme",
      "tags": [
        "TyreSets"
      ],
      "ready": true,
      "description": "Pneumatici in deposito e passaggi di stato."
    },
    {
      "slug": "fornitori",
      "name": "Fornitori",
      "tags": [
        "Suppliers"
      ],
      "ready": true,
      "description": "Fornitori, ordini e documenti di trasporto."
    },
    {
      "slug": "spese",
      "name": "Spese",
      "tags": [
        "Expenses"
      ],
      "ready": true,
      "description": "Spese, pagamenti e categorie."
    },
    {
      "slug": "prima-nota",
      "name": "Prima nota",
      "tags": [
        "CashBook"
      ],
      "ready": true,
      "description": "Movimenti di cassa e conti finanziari."
    },
    {
      "slug": "impostazioni",
      "name": "Impostazioni",
      "tags": [
        "Settings"
      ],
      "ready": true,
      "description": "Dati azienda, aliquote IVA, listini, collaboratori."
    },
    {
      "slug": "fatturazione-elettronica",
      "name": "Fatturazione elettronica",
      "tags": [
        "ElectronicInvoicing"
      ],
      "ready": true,
      "description": "Fatture elettroniche emesse e ricevute, in sola lettura."
    },
    {
      "slug": "concessionaria",
      "name": "Concessionaria",
      "tags": [
        "Dealership"
      ],
      "ready": true,
      "description": "Stock veicoli, vendite e report."
    },
    {
      "slug": "report",
      "name": "Report",
      "tags": [
        "Reports"
      ],
      "ready": true,
      "description": "Statistiche e contatori della tua officina."
    },
    {
      "slug": "autenticazione",
      "name": "Autenticazione",
      "tags": [
        "Authentication"
      ],
      "ready": true,
      "description": "Verifica della chiave e dei permessi."
    }
  ],
  "paths": {
    "/v1/articles": {
      "parameters": [
        {
          "$ref": "#/components/parameters/APIVersion"
        }
      ],
      "get": {
        "tags": [
          "Articles"
        ],
        "summary": "Search the article/labor catalog",
        "x-summary-it": "Cerca nel magazzino e nel listino",
        "x-scope": "articles.read",
        "x-description-it": "Il catalogo dietro «Cerca da magazzino». Contiene sia i ricambi\n(`kind: item`) sia le voci di manodopera (`kind: work`).\n\nOgni parola di `q` deve comparire nel nome, nel codice o nelle note.\nFiltra per tipo con `kind`. Passa `customer_id` per ottenere il\n`sale_price` già scontato secondo il listino di quel cliente.\n",
        "description": "The catalog behind the line-item editor's \"Cerca da magazzino\".\nArticles back both the magazzino (parts — `kind: item`) and the\nlistino manodopera (labor — `kind: work`).\n\nEvery whitespace-separated term in `?q=` must match the name,\ncode, or comments (results are relevance-ranked while searching,\nname-sorted while browsing). Pass `?customer_id=` so\n`sale_price` reflects the customer's price-list overrides.\n",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "kind",
            "in": "query",
            "description": "Restrict to parts (\"item\") or labor (\"work\"); omitted = both.",
            "schema": {
              "type": "string",
              "enum": [
                "item",
                "work"
              ]
            }
          },
          {
            "name": "customer_id",
            "in": "query",
            "description": "Apply this customer's price-list overrides to sale_price.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 25,
              "maximum": 50
            }
          },
          {
            "name": "starting_after",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "supplier_id",
            "in": "query",
            "required": false,
            "description": "Filtra per fornitore.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "One page of catalog rows.",
            "x-description-it": "Gli articoli che soddisfano la ricerca.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "object",
                    "data",
                    "has_more",
                    "url"
                  ],
                  "properties": {
                    "object": {
                      "type": "string",
                      "const": "list"
                    },
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Article"
                      }
                    },
                    "has_more": {
                      "type": "boolean"
                    },
                    "next_starting_after": {
                      "type": "string"
                    },
                    "url": {
                      "type": "string"
                    }
                  }
                },
                "example": {
                  "object": "list",
                  "data": [
                    {
                      "object": "article",
                      "id": "f18c4b62-9d07-4e53-a294-6b1f8d3c5e70",
                      "code": "FO-2231",
                      "name": "Filtro olio",
                      "kind": "item",
                      "unit_type": "pz",
                      "stock": 12,
                      "sale_price": 14.9,
                      "vat_code": "22",
                      "unit_discount_rate": 0
                    },
                    {
                      "object": "article",
                      "id": "2c9e5a71-4b38-40df-9126-7e3d1f6a8c25",
                      "code": null,
                      "name": "Manodopera meccanica",
                      "kind": "work",
                      "unit_type": "h",
                      "stock": 0,
                      "sale_price": 40.0,
                      "vat_code": "22",
                      "unit_discount_rate": 0
                    }
                  ],
                  "has_more": false,
                  "url": "/v1/articles"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      },
      "post": {
        "tags": [
          "Articles"
        ],
        "summary": "Create an article",
        "x-summary-it": "Crea un articolo",
        "x-scope": "articles.write",
        "x-description-it": "Crea un ricambio o una voce di manodopera. `unit_type` deve essere\ncoerente con il tipo: `kg`, `L`, `kit`, `pz` per il magazzino;\n`gg`, `h`, `lav`, `min` per la manodopera. `vat_code` deve essere una\ndelle aliquote attive dell'azienda (vedi `GET /v1/vat-rates`).\n",
        "operationId": "articleCreate",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "name",
                  "unit_type",
                  "vat_code"
                ],
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "code": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "unit_type": {
                    "type": "string",
                    "description": "Magazzino: `kg`, `L`, `kit`, `pz`. Manodopera: `gg`, `h`,\n`lav`, `min`.\n"
                  },
                  "stock": {
                    "type": [
                      "number",
                      "null"
                    ]
                  },
                  "minimum_stock": {
                    "type": [
                      "number",
                      "null"
                    ]
                  },
                  "purchase_price": {
                    "type": [
                      "number",
                      "null"
                    ]
                  },
                  "sale_price": {
                    "type": [
                      "number",
                      "null"
                    ]
                  },
                  "vat_code": {
                    "type": "string",
                    "description": "Deve essere una delle aliquote attive dell'azienda."
                  },
                  "unit_discount_rate": {
                    "type": [
                      "integer",
                      "null"
                    ]
                  },
                  "location": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "comments": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "supplier_id": {
                    "type": [
                      "string",
                      "null"
                    ]
                  }
                }
              },
              "example": {
                "name": "Filtro olio",
                "code": "FO-2231",
                "unit_type": "pz",
                "stock": 12,
                "purchase_price": 6.4,
                "sale_price": 14.9,
                "vat_code": "22",
                "supplier_id": null
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "success",
            "x-description-it": "L'articolo creato.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/json": {
                "example": {
                  "object": "article",
                  "id": "f18c4b62-9d07-4e53-a294-6b1f8d3c5e70",
                  "code": "FO-2231",
                  "name": "Filtro olio",
                  "kind": "item",
                  "unit_type": "pz",
                  "stock": 12,
                  "sale_price": 14.9,
                  "vat_code": "22",
                  "unit_discount_rate": 0,
                  "minimum_stock": 4,
                  "purchase_price": 6.4,
                  "location": "Scaffale A2",
                  "comments": "Compatibile con motori 1.2 FIRE",
                  "supplier_id": "6f2b8c14-9d70-4a35-b8e2-1c5f7a3d9e60",
                  "supplier_name": "Ricambi Lombardia S.r.l."
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/articles/{id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/APIVersion"
        },
        {
          "name": "id",
          "in": "path",
          "required": true,
          "description": "Identifier of the target resource.",
          "schema": {
            "type": "string"
          }
        }
      ],
      "delete": {
        "tags": [
          "Articles"
        ],
        "summary": "Delete article",
        "x-summary-it": "Elimina un articolo",
        "x-scope": "articles.write",
        "x-description-it": "Eliminazione logica: l'articolo sparisce dal catalogo. Le righe dei\ndocumenti che lo citano restano intatte.\n",
        "operationId": "articleDelete",
        "responses": {
          "200": {
            "description": "success",
            "x-description-it": "L'articolo eliminato.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/json": {
                "example": {
                  "object": "article",
                  "id": "f18c4b62-9d07-4e53-a294-6b1f8d3c5e70",
                  "code": "FO-2231",
                  "name": "Filtro olio",
                  "kind": "item",
                  "unit_type": "pz",
                  "stock": 12,
                  "sale_price": 14.9,
                  "vat_code": "22",
                  "unit_discount_rate": 0,
                  "minimum_stock": 4,
                  "purchase_price": 6.4,
                  "location": "Scaffale A2",
                  "comments": "Compatibile con motori 1.2 FIRE",
                  "supplier_id": "6f2b8c14-9d70-4a35-b8e2-1c5f7a3d9e60",
                  "supplier_name": "Ricambi Lombardia S.r.l."
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      },
      "get": {
        "tags": [
          "Articles"
        ],
        "summary": "Retrieve a article",
        "x-summary-it": "Recupera un articolo",
        "x-scope": "articles.read",
        "x-description-it": "Restituisce l'articolo con i campi di dettaglio che l'elenco omette:\nsoglia di riordino, prezzo di acquisto, posizione, note e fornitore.\n",
        "operationId": "articleGet",
        "responses": {
          "200": {
            "description": "success",
            "x-description-it": "L'articolo richiesto.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/json": {
                "example": {
                  "object": "article",
                  "id": "f18c4b62-9d07-4e53-a294-6b1f8d3c5e70",
                  "code": "FO-2231",
                  "name": "Filtro olio",
                  "kind": "item",
                  "unit_type": "pz",
                  "stock": 12,
                  "sale_price": 14.9,
                  "vat_code": "22",
                  "unit_discount_rate": 0,
                  "minimum_stock": 4,
                  "purchase_price": 6.4,
                  "location": "Scaffale A2",
                  "comments": "Compatibile con motori 1.2 FIRE",
                  "supplier_id": "6f2b8c14-9d70-4a35-b8e2-1c5f7a3d9e60",
                  "supplier_name": "Ricambi Lombardia S.r.l."
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      },
      "patch": {
        "tags": [
          "Articles"
        ],
        "summary": "Update article",
        "x-summary-it": "Aggiorna un articolo",
        "x-scope": "articles.write",
        "x-description-it": "Il corpo è lo stesso della creazione: va inviato l'oggetto completo,\nnon solo i campi da cambiare. I campi omessi vengono azzerati, non\nlasciati invariati.\n",
        "operationId": "articlePatch",
        "description": "(the mobile edit sends every field, matching the web editor).\n",
        "requestBody": {
          "required": true,
          "description": "Il corpo è lo stesso della creazione: va inviato\nl'oggetto completo, non solo i campi da cambiare.\n",
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "name",
                  "unit_type",
                  "vat_code"
                ],
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "code": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "unit_type": {
                    "type": "string",
                    "description": "Magazzino: `kg`, `L`, `kit`, `pz`. Manodopera: `gg`, `h`,\n`lav`, `min`.\n"
                  },
                  "stock": {
                    "type": [
                      "number",
                      "null"
                    ]
                  },
                  "minimum_stock": {
                    "type": [
                      "number",
                      "null"
                    ]
                  },
                  "purchase_price": {
                    "type": [
                      "number",
                      "null"
                    ]
                  },
                  "sale_price": {
                    "type": [
                      "number",
                      "null"
                    ]
                  },
                  "vat_code": {
                    "type": "string",
                    "description": "Deve essere una delle aliquote attive dell'azienda."
                  },
                  "unit_discount_rate": {
                    "type": [
                      "integer",
                      "null"
                    ]
                  },
                  "location": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "comments": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "supplier_id": {
                    "type": [
                      "string",
                      "null"
                    ]
                  }
                }
              },
              "example": {
                "name": "Filtro olio",
                "code": "FO-2231",
                "unit_type": "pz",
                "stock": 12,
                "purchase_price": 6.4,
                "sale_price": 14.9,
                "vat_code": "22",
                "supplier_id": null
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "success",
            "x-description-it": "L'articolo aggiornato.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/json": {
                "example": {
                  "object": "article",
                  "id": "f18c4b62-9d07-4e53-a294-6b1f8d3c5e70",
                  "code": "FO-2231",
                  "name": "Filtro olio",
                  "kind": "item",
                  "unit_type": "pz",
                  "stock": 12,
                  "sale_price": 14.9,
                  "vat_code": "22",
                  "unit_discount_rate": 0,
                  "minimum_stock": 4,
                  "purchase_price": 6.4,
                  "location": "Scaffale A2",
                  "comments": "Compatibile con motori 1.2 FIRE",
                  "supplier_id": "6f2b8c14-9d70-4a35-b8e2-1c5f7a3d9e60",
                  "supplier_name": "Ricambi Lombardia S.r.l."
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/articles/{id}/movements": {
      "parameters": [
        {
          "$ref": "#/components/parameters/APIVersion"
        },
        {
          "name": "id",
          "in": "path",
          "required": true,
          "description": "Identifier of the target resource.",
          "schema": {
            "type": "string"
          }
        }
      ],
      "get": {
        "tags": [
          "Articles"
        ],
        "summary": "Article movements",
        "x-summary-it": "Movimenti di un articolo",
        "x-scope": "articles.read",
        "x-description-it": "Lo storico di carico e scarico dell'articolo. `quantity` è positiva\nper i carichi e negativa per gli scarichi; `source` indica il\ndocumento che ha generato il movimento e `source_id` lo identifica.\n",
        "operationId": "articleMovements",
        "description": "The \"Storico lavorazioni\" for an article / lavorazione: the sale\nmovements where the item was used (worksheet line items, order items,\nstandalone receipts), newest first — same set as the web service-catalog\ndetail (sale-type only). Unpaginated (a handful of rows).\n",
        "responses": {
          "200": {
            "description": "success",
            "x-description-it": "I movimenti dell'articolo.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ListEnvelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/ArticleMovement"
                          }
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "object": "list",
                  "data": [
                    {
                      "object": "article_movement",
                      "id": "8b4d2f06-1c73-4a95-b028-5e7f3a1d9c64",
                      "quantity": -4,
                      "source": "worksheet",
                      "source_id": "a5d21e64-8f37-4b90-ae12-3c7d6b048f95",
                      "seq_number": 412,
                      "document_date": "2026-07-15",
                      "customer_name": "Mario Rossi",
                      "vehicle_name": "Fiat Panda",
                      "total": 74.0,
                      "created_at": "2026-07-15T08:31:00Z"
                    },
                    {
                      "object": "article_movement",
                      "id": "3f7a9c25-6d81-4e40-9b53-2a8c1f4d7e06",
                      "quantity": 20,
                      "source": "delivery_note",
                      "source_id": "9d1e6b48-5c20-4f37-a814-3b7e2d9f5a61",
                      "seq_number": 77,
                      "document_date": "2026-07-02",
                      "customer_name": null,
                      "vehicle_name": null,
                      "total": 128.0,
                      "created_at": "2026-07-02T11:14:00Z"
                    }
                  ],
                  "has_more": false,
                  "url": "/v1/articles/f18c4b62-9d07-4e53-a294-6b1f8d3c5e70/movements"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/calendar-entries": {
      "parameters": [
        {
          "$ref": "#/components/parameters/APIVersion"
        }
      ],
      "get": {
        "tags": [
          "Calendar"
        ],
        "summary": "List calendar entries",
        "x-summary-it": "Elenca gli appuntamenti",
        "x-scope": "calendar.read",
        "x-description-it": "Gli appuntamenti in agenda. Filtra per intervallo con `from` / `to`,\nper calendario con `calendar_id` e per collaboratore con `user_id`.\nOgni voce include cliente, veicolo e calendario già risolti, senza\nchiamate aggiuntive, e l'eventuale scheda lavoro collegata in\n`worksheet_id`.\n",
        "description": "Returns calendar entries scoped to the authenticated user's\ncompany.\n\nFilter dimensions are mutually exclusive — the first match\nwins, in order: `customer_id`, `vehicle_id`, then the default\ntimeframe. When no filter is supplied, returns entries between\ntoday (UTC) and today + 30 days across all calendars.\n\nPagination: `limit` clamps at 100 and `starting_after` is\naccepted for forward-compatibility. Today the underlying\nrepository does not support cursor pagination —\n`starting_after` is decoded for input validation but otherwise\nignored, and `has_more` reflects whether the unsliced result\nset exceeded `limit`. Clients should treat the cursor as a\nstable surface that will gain semantics in a future dated\nrevision.\n",
        "parameters": [
          {
            "name": "customer_id",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "vehicle_id",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "calendar_id",
            "in": "query",
            "required": false,
            "description": "Restrict the timeframe listing to one parent calendar. Send it\nempty (`calendar_id=`) or omit it for \"all calendars\" — this tier\nstores no selection between requests. The effective selection is\nechoed in `calendar_filter`. Ignored when filtering by\n`customer_id` / `vehicle_id`.\n",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "from",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "to",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 20
            }
          },
          {
            "name": "starting_after",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Page of calendar entries.",
            "x-description-it": "Gli appuntamenti nell'intervallo richiesto.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ListEnvelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/CalendarEntry"
                          }
                        },
                        "calendar_filter": {
                          "allOf": [
                            {
                              "$ref": "#/components/schemas/CalendarFilter"
                            }
                          ],
                          "description": "The effective calendar selection that was applied.\nPresent only on the default timeframe listing;\nomitted when filtering by `customer_id` / `vehicle_id`.\n"
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "object": "list",
                  "data": [
                    {
                      "object": "calendar_entry",
                      "id": "c72e9a41-8b35-4d07-9e62-1f4a8c3d5b90",
                      "title": "Tagliando Fiat Panda",
                      "description": "Cliente lascia l'auto alle 8:30.",
                      "start_time": "2026-08-12T08:30:00Z",
                      "end_time": "2026-08-12T10:00:00Z",
                      "color": "#2563eb",
                      "calendar_id": "a18f3c65-2d90-4b47-8e13-6c9b2f5a7d04",
                      "user_id": "4e7b1d29-5a63-40c8-9f15-2b8d6e3a1c74",
                      "customer_id": "4c911f08-7713-4e83-bdf6-a0692a1b43fd",
                      "vehicle_id": "8821f0ac-5d2b-41e7-9a10-6c3f2b7d4e55",
                      "worksheet_id": "a5d21e64-8f37-4b90-ae12-3c7d6b048f95",
                      "quote_id": null,
                      "checkin_id": null,
                      "work_category_id": "3a8e5c21-9d76-4f03-b418-2c6a9e7d5b40",
                      "work_category_name": "Tagliando",
                      "email_reminder": false,
                      "auto_reminder_enabled": false,
                      "auto_reminder_channel": null,
                      "auto_reminder_sent_at": null,
                      "customer": {
                        "object": "customer",
                        "id": "4c911f08-7713-4e83-bdf6-a0692a1b43fd",
                        "full_name": "Mario Rossi"
                      },
                      "vehicle": {
                        "object": "vehicle",
                        "id": "8821f0ac-5d2b-41e7-9a10-6c3f2b7d4e55",
                        "model": "Panda",
                        "license": "AB123CD"
                      },
                      "calendar": {
                        "object": "calendar",
                        "id": "a18f3c65-2d90-4b47-8e13-6c9b2f5a7d04",
                        "name": "Officina 1"
                      },
                      "user": {
                        "object": "user",
                        "id": "4e7b1d29-5a63-40c8-9f15-2b8d6e3a1c74",
                        "name": "Luca Bianchi"
                      },
                      "deleted_at": null,
                      "created_at": "2026-08-01T09:00:00Z",
                      "updated_at": "2026-08-01T09:00:00Z"
                    }
                  ],
                  "has_more": false,
                  "url": "/v1/calendar-entries"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMediaType"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      },
      "post": {
        "tags": [
          "Calendar"
        ],
        "summary": "Create a calendar entry",
        "x-summary-it": "Crea un appuntamento",
        "x-scope": "calendar.write",
        "x-description-it": "Fissa un appuntamento. Servono `start_time` e `end_time` in RFC 3339;\ncollega il lavoro con `customer_id`, `vehicle_id` e `worksheet_id`.\n\nCon `worksheet_id` l'appuntamento viene collegato alla scheda lavoro:\nse ometti `customer_id` o `vehicle_id` vengono presi dalla scheda; se\nli invii devono essere quelli della scheda (una scheda senza veicolo\nva bene per qualsiasi veicolo del suo cliente), altrimenti la\nrichiesta risponde `422` su `/worksheet_id`. Per inviare\n`worksheet_id` la chiave deve avere anche il permesso\n`worksheets.read`, altrimenti la richiesta risponde `403`.\n\nI promemoria automatici via SMS o WhatsApp non sono disponibili via\nAPI: inviano messaggi ai tuoi clienti e consumano crediti, quindi una\nrichiesta con `auto_reminder_enabled: true` e un canale a pagamento\nviene rifiutata con `403`. Il canale `email` resta ammesso perché usa\nil tuo mittente e non costa crediti.\n",
        "description": "Creates an appointment owned by the authenticated user inside\ntheir company. `end_time` must be strictly after `start_time`.\nThe response is the fully materialized entry (denormalized\ncustomer / vehicle / calendar / user fields included) so the\nclient can render the new row without a follow-up GET.\n\nForeign-key references in the body (`customer_id`, `vehicle_id`,\n`worksheet_id`, `work_category_id`, `calendar_id`) must resolve\nto resources owned by the authenticated user's company.\nUnreachable refs return 422 `parameter_invalid` pointing at the\noffending field.\n\n`worksheet_id` links the appointment to a scheda lavoro. Omit\n`customer_id` / `vehicle_id` to take them from the worksheet;\nwhen sent, they must be the worksheet's (a worksheet without a\nvehicle fits any vehicle of its customer), otherwise the request\nreturns 422 `parameter_invalid` on `/worksheet_id`. Sending\n`worksheet_id` needs `worksheets.read` (the key's scope, or the\napp user's permission); without it the request returns 403.\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CalendarEntryCreateRequest"
              },
              "example": {
                "title": "Tagliando Fiat Panda",
                "description": "Cambio olio + filtri.",
                "start_time": "2026-06-12T08:00:00Z",
                "end_time": "2026-06-12T10:00:00Z",
                "color": "#2563eb",
                "worksheet_id": "33333333-3333-3333-3333-333333333333",
                "email_reminder": true
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Calendar entry created.",
            "x-description-it": "L'appuntamento creato.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CalendarEntry"
                },
                "example": {
                  "object": "calendar_entry",
                  "id": "c72e9a41-8b35-4d07-9e62-1f4a8c3d5b90",
                  "title": "Tagliando Fiat Panda",
                  "description": "Cliente lascia l'auto alle 8:30.",
                  "start_time": "2026-08-12T08:30:00Z",
                  "end_time": "2026-08-12T10:00:00Z",
                  "color": "#2563eb",
                  "calendar_id": "a18f3c65-2d90-4b47-8e13-6c9b2f5a7d04",
                  "user_id": "4e7b1d29-5a63-40c8-9f15-2b8d6e3a1c74",
                  "customer_id": "4c911f08-7713-4e83-bdf6-a0692a1b43fd",
                  "vehicle_id": "8821f0ac-5d2b-41e7-9a10-6c3f2b7d4e55",
                  "worksheet_id": "a5d21e64-8f37-4b90-ae12-3c7d6b048f95",
                  "quote_id": null,
                  "checkin_id": null,
                  "work_category_id": "3a8e5c21-9d76-4f03-b418-2c6a9e7d5b40",
                  "work_category_name": "Tagliando",
                  "email_reminder": false,
                  "auto_reminder_enabled": false,
                  "auto_reminder_channel": null,
                  "auto_reminder_sent_at": null,
                  "customer": {
                    "object": "customer",
                    "id": "4c911f08-7713-4e83-bdf6-a0692a1b43fd",
                    "full_name": "Mario Rossi"
                  },
                  "vehicle": {
                    "object": "vehicle",
                    "id": "8821f0ac-5d2b-41e7-9a10-6c3f2b7d4e55",
                    "model": "Panda",
                    "license": "AB123CD"
                  },
                  "calendar": {
                    "object": "calendar",
                    "id": "a18f3c65-2d90-4b47-8e13-6c9b2f5a7d04",
                    "name": "Officina 1"
                  },
                  "user": {
                    "object": "user",
                    "id": "4e7b1d29-5a63-40c8-9f15-2b8d6e3a1c74",
                    "name": "Luca Bianchi"
                  },
                  "deleted_at": null,
                  "created_at": "2026-08-01T09:00:00Z",
                  "updated_at": "2026-08-01T09:00:00Z"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/MalformedRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMediaType"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/calendar-entries/{id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/APIVersion"
        },
        {
          "name": "id",
          "in": "path",
          "required": true,
          "description": "Calendar entry id (UUID).",
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        }
      ],
      "get": {
        "tags": [
          "Calendar"
        ],
        "summary": "Retrieve a calendar entry",
        "x-summary-it": "Recupera un appuntamento",
        "x-scope": "calendar.read",
        "x-description-it": "Restituisce il singolo appuntamento con cliente, veicolo, calendario\ne collaboratore già risolti.\n",
        "description": "Returns the entry if it belongs to the caller's company.\nUnknown, cross-tenant, and soft-deleted IDs all return 404 —\nnever 403 — to avoid leaking the existence of resources in\nother tenants.\n",
        "responses": {
          "200": {
            "description": "Calendar entry found.",
            "x-description-it": "L'appuntamento richiesto.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CalendarEntry"
                },
                "example": {
                  "object": "calendar_entry",
                  "id": "c72e9a41-8b35-4d07-9e62-1f4a8c3d5b90",
                  "title": "Tagliando Fiat Panda",
                  "description": "Cliente lascia l'auto alle 8:30.",
                  "start_time": "2026-08-12T08:30:00Z",
                  "end_time": "2026-08-12T10:00:00Z",
                  "color": "#2563eb",
                  "calendar_id": "a18f3c65-2d90-4b47-8e13-6c9b2f5a7d04",
                  "user_id": "4e7b1d29-5a63-40c8-9f15-2b8d6e3a1c74",
                  "customer_id": "4c911f08-7713-4e83-bdf6-a0692a1b43fd",
                  "vehicle_id": "8821f0ac-5d2b-41e7-9a10-6c3f2b7d4e55",
                  "worksheet_id": "a5d21e64-8f37-4b90-ae12-3c7d6b048f95",
                  "quote_id": null,
                  "checkin_id": null,
                  "work_category_id": "3a8e5c21-9d76-4f03-b418-2c6a9e7d5b40",
                  "work_category_name": "Tagliando",
                  "email_reminder": false,
                  "auto_reminder_enabled": false,
                  "auto_reminder_channel": null,
                  "auto_reminder_sent_at": null,
                  "customer": {
                    "object": "customer",
                    "id": "4c911f08-7713-4e83-bdf6-a0692a1b43fd",
                    "full_name": "Mario Rossi"
                  },
                  "vehicle": {
                    "object": "vehicle",
                    "id": "8821f0ac-5d2b-41e7-9a10-6c3f2b7d4e55",
                    "model": "Panda",
                    "license": "AB123CD"
                  },
                  "calendar": {
                    "object": "calendar",
                    "id": "a18f3c65-2d90-4b47-8e13-6c9b2f5a7d04",
                    "name": "Officina 1"
                  },
                  "user": {
                    "object": "user",
                    "id": "4e7b1d29-5a63-40c8-9f15-2b8d6e3a1c74",
                    "name": "Luca Bianchi"
                  },
                  "deleted_at": null,
                  "created_at": "2026-08-01T09:00:00Z",
                  "updated_at": "2026-08-01T09:00:00Z"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMediaType"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      },
      "patch": {
        "tags": [
          "Calendar"
        ],
        "summary": "Update a calendar entry",
        "x-summary-it": "Aggiorna un appuntamento",
        "x-scope": "calendar.write",
        "x-description-it": "Aggiorna solo i campi presenti nel corpo: gli altri restano\ninvariati.\n\n`worksheet_id` collega l'appuntamento a una scheda lavoro e `null`\nlo scollega. Cliente e veicolo risultanti devono essere quelli della\nscheda (una scheda senza veicolo va bene per qualsiasi veicolo del\nsuo cliente): quelli mancanti vengono presi dalla scheda, altrimenti\nla richiesta risponde `422` su `/worksheet_id`. Se cambi\n`customer_id` o `vehicle_id` senza inviare `worksheet_id` e la\nscheda collegata non corrisponde più, l'appuntamento viene\nscollegato e la risposta riporta `worksheet_id: null`. Per inviare\n`worksheet_id` la chiave deve avere anche il permesso\n`worksheets.read`, altrimenti la richiesta risponde `403`.\n\nValgono gli stessi limiti sui promemoria a pagamento della creazione.\nIn più, un appuntamento che ha già un promemoria automatico attivo\nnon è modificabile via API: la modifica potrebbe far partire un nuovo\nmessaggio al cliente, quindi la richiesta risponde `403` e va fatta\ndall'app.\n",
        "description": "Three-state PATCH semantics (omitted = unchanged, `null` =\nclear nullable, value = set).\n\nSending `null` on a non-nullable field (`title`, `color`,\n`start_time`, `end_time`, `email_reminder`,\n`auto_reminder_enabled`) returns 422 `parameter_invalid`. When\nthe patched pair of timestamps violates\n`end_time \u003e start_time` the request is rejected with 422.\n\n`user_id` (ownership), `company_id`, `auto_reminder_sent_at`,\nand the audit timestamps are not patchable through this\nendpoint.\n\nForeign-key references in the body (`customer_id`, `vehicle_id`,\n`worksheet_id`, `work_category_id`, `calendar_id`) must resolve\nto resources owned by the authenticated user's company.\nUnreachable refs return 422 `parameter_invalid` pointing at the\noffending field.\n\n`worksheet_id` links the appointment to a scheda lavoro and\n`null` unlinks it. The customer and vehicle left after the patch\nmust be the worksheet's (a worksheet without a vehicle fits any\nvehicle of its customer); the ones the entry is missing are\ntaken from the worksheet, and a mismatch returns 422\n`parameter_invalid` on `/worksheet_id`. A patch that moves\n`customer_id` or `vehicle_id` off the linked worksheet, without\nsending `worksheet_id`, unlinks it: the response then carries\n`worksheet_id: null`. Sending `worksheet_id` (value or `null`)\nneeds `worksheets.read` (the key's scope, or the app user's\npermission); without it the request returns 403.\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CalendarEntryPatchRequest"
              },
              "example": {
                "title": "Tagliando Fiat Panda — riprogrammato",
                "start_time": "2026-06-13T09:00:00Z",
                "end_time": "2026-06-13T11:00:00Z",
                "description": null
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Calendar entry updated.",
            "x-description-it": "L'appuntamento aggiornato.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CalendarEntry"
                },
                "example": {
                  "object": "calendar_entry",
                  "id": "c72e9a41-8b35-4d07-9e62-1f4a8c3d5b90",
                  "title": "Tagliando Fiat Panda",
                  "description": "Cliente lascia l'auto alle 8:30.",
                  "start_time": "2026-08-12T08:30:00Z",
                  "end_time": "2026-08-12T10:00:00Z",
                  "color": "#2563eb",
                  "calendar_id": "a18f3c65-2d90-4b47-8e13-6c9b2f5a7d04",
                  "user_id": "4e7b1d29-5a63-40c8-9f15-2b8d6e3a1c74",
                  "customer_id": "4c911f08-7713-4e83-bdf6-a0692a1b43fd",
                  "vehicle_id": "8821f0ac-5d2b-41e7-9a10-6c3f2b7d4e55",
                  "worksheet_id": "a5d21e64-8f37-4b90-ae12-3c7d6b048f95",
                  "quote_id": null,
                  "checkin_id": null,
                  "work_category_id": "3a8e5c21-9d76-4f03-b418-2c6a9e7d5b40",
                  "work_category_name": "Tagliando",
                  "email_reminder": false,
                  "auto_reminder_enabled": false,
                  "auto_reminder_channel": null,
                  "auto_reminder_sent_at": null,
                  "customer": {
                    "object": "customer",
                    "id": "4c911f08-7713-4e83-bdf6-a0692a1b43fd",
                    "full_name": "Mario Rossi"
                  },
                  "vehicle": {
                    "object": "vehicle",
                    "id": "8821f0ac-5d2b-41e7-9a10-6c3f2b7d4e55",
                    "model": "Panda",
                    "license": "AB123CD"
                  },
                  "calendar": {
                    "object": "calendar",
                    "id": "a18f3c65-2d90-4b47-8e13-6c9b2f5a7d04",
                    "name": "Officina 1"
                  },
                  "user": {
                    "object": "user",
                    "id": "4e7b1d29-5a63-40c8-9f15-2b8d6e3a1c74",
                    "name": "Luca Bianchi"
                  },
                  "deleted_at": null,
                  "created_at": "2026-08-01T09:00:00Z",
                  "updated_at": "2026-08-01T09:00:00Z"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/MalformedRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMediaType"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      },
      "delete": {
        "tags": [
          "Calendar"
        ],
        "summary": "Soft-delete a calendar entry",
        "x-summary-it": "Elimina un appuntamento",
        "x-scope": "calendar.write",
        "x-description-it": "Eliminazione logica dell'appuntamento.\n",
        "description": "Stamps `deleted_at` with NOW(). The row remains in the\ndatabase but is no longer addressable through the mobile API —\nsubsequent GET/PATCH/DELETE return 404. Returns the deleted\nentry (200) so clients can render an undo banner with the\npopulated `deleted_at`.\n",
        "responses": {
          "200": {
            "description": "Calendar entry soft-deleted.",
            "x-description-it": "L'appuntamento eliminato.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CalendarEntry"
                },
                "example": {
                  "object": "calendar_entry",
                  "id": "c72e9a41-8b35-4d07-9e62-1f4a8c3d5b90",
                  "title": "Tagliando Fiat Panda",
                  "description": "Cliente lascia l'auto alle 8:30.",
                  "start_time": "2026-08-12T08:30:00Z",
                  "end_time": "2026-08-12T10:00:00Z",
                  "color": "#2563eb",
                  "calendar_id": "a18f3c65-2d90-4b47-8e13-6c9b2f5a7d04",
                  "user_id": "4e7b1d29-5a63-40c8-9f15-2b8d6e3a1c74",
                  "customer_id": "4c911f08-7713-4e83-bdf6-a0692a1b43fd",
                  "vehicle_id": "8821f0ac-5d2b-41e7-9a10-6c3f2b7d4e55",
                  "worksheet_id": "a5d21e64-8f37-4b90-ae12-3c7d6b048f95",
                  "quote_id": null,
                  "checkin_id": null,
                  "work_category_id": "3a8e5c21-9d76-4f03-b418-2c6a9e7d5b40",
                  "work_category_name": "Tagliando",
                  "email_reminder": false,
                  "auto_reminder_enabled": false,
                  "auto_reminder_channel": null,
                  "auto_reminder_sent_at": null,
                  "customer": {
                    "object": "customer",
                    "id": "4c911f08-7713-4e83-bdf6-a0692a1b43fd",
                    "full_name": "Mario Rossi"
                  },
                  "vehicle": {
                    "object": "vehicle",
                    "id": "8821f0ac-5d2b-41e7-9a10-6c3f2b7d4e55",
                    "model": "Panda",
                    "license": "AB123CD"
                  },
                  "calendar": {
                    "object": "calendar",
                    "id": "a18f3c65-2d90-4b47-8e13-6c9b2f5a7d04",
                    "name": "Officina 1"
                  },
                  "user": {
                    "object": "user",
                    "id": "4e7b1d29-5a63-40c8-9f15-2b8d6e3a1c74",
                    "name": "Luca Bianchi"
                  },
                  "created_at": "2026-08-01T09:00:00Z",
                  "updated_at": "2026-08-01T09:00:00Z",
                  "deleted_at": "2026-08-05T11:04:00Z"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/calendars": {
      "parameters": [
        {
          "$ref": "#/components/parameters/APIVersion"
        }
      ],
      "get": {
        "tags": [
          "Calendar"
        ],
        "summary": "List calendars",
        "x-summary-it": "Elenca i calendari",
        "x-scope": "calendar.read",
        "x-description-it": "I calendari configurati dall'officina, da usare per valorizzare\n`calendar_id`. L'elenco è breve e non paginato.\n",
        "description": "Returns every calendar container in the company, unpaginated\n(workshops have a handful at most). The mobile calendar screen\nuses them for the \"Calendario\" filter sheet and to resolve\nentry colours; filter entries with `?calendar_id=` on\n`GET /v1/calendar-entries`.\n",
        "responses": {
          "200": {
            "description": "All calendars.",
            "x-description-it": "I calendari configurati.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ListEnvelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/Calendar"
                          }
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "object": "list",
                  "data": [
                    {
                      "object": "calendar",
                      "id": "a18f3c65-2d90-4b47-8e13-6c9b2f5a7d04",
                      "name": "Officina 1",
                      "default_color": "#2563eb"
                    },
                    {
                      "object": "calendar",
                      "id": "6d2b9f14-7c58-4a03-b91e-3f5a8d2c6e47",
                      "name": "Carrozzeria",
                      "default_color": "#16a34a"
                    }
                  ],
                  "has_more": false,
                  "url": "/v1/calendars"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      },
      "post": {
        "tags": [
          "Calendar"
        ],
        "summary": "Create a calendar",
        "x-summary-it": "Crea un calendario",
        "x-scope": "calendar.write",
        "x-description-it": "Crea un calendario. `default_color` è il colore esadecimale usato in\nagenda per le voci che non ne specificano uno.\n",
        "operationId": "calendarCreate",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "name"
                ],
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "default_color": {
                    "type": "string",
                    "description": "Colore esadecimale usato in agenda, es. `#2563eb`."
                  }
                }
              },
              "example": {
                "name": "Officina 1",
                "default_color": "#2563eb"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "success",
            "x-description-it": "Il calendario creato.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/json": {
                "example": {
                  "object": "calendar",
                  "id": "a18f3c65-2d90-4b47-8e13-6c9b2f5a7d04",
                  "name": "Officina 1",
                  "default_color": "#2563eb"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/calendars/{id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/APIVersion"
        },
        {
          "name": "id",
          "in": "path",
          "required": true,
          "description": "Identifier of the target resource.",
          "schema": {
            "type": "string"
          }
        }
      ],
      "delete": {
        "tags": [
          "Calendar"
        ],
        "summary": "Delete calendar",
        "x-summary-it": "Elimina un calendario",
        "x-scope": "calendar.write",
        "x-description-it": "Elimina il calendario. Gli appuntamenti che vi appartenevano\nvengono riassegnati a un altro calendario.\n",
        "operationId": "calendarDelete",
        "responses": {
          "200": {
            "description": "success",
            "x-description-it": "Il calendario eliminato.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/json": {
                "example": {
                  "object": "calendar",
                  "id": "a18f3c65-2d90-4b47-8e13-6c9b2f5a7d04"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      },
      "patch": {
        "tags": [
          "Calendar"
        ],
        "summary": "Update calendar",
        "x-summary-it": "Aggiorna un calendario",
        "x-scope": "calendar.write",
        "x-description-it": "Aggiorna nome e colore predefinito del calendario.\n",
        "operationId": "calendarPatch",
        "requestBody": {
          "required": true,
          "description": "Il corpo è lo stesso della creazione: va inviato\nl'oggetto completo, non solo i campi da cambiare.\n",
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "name"
                ],
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "default_color": {
                    "type": "string",
                    "description": "Colore esadecimale usato in agenda, es. `#2563eb`."
                  }
                }
              },
              "example": {
                "name": "Officina 1",
                "default_color": "#2563eb"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "success",
            "x-description-it": "Il calendario aggiornato.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/json": {
                "example": {
                  "object": "calendar",
                  "id": "a18f3c65-2d90-4b47-8e13-6c9b2f5a7d04",
                  "name": "Officina 1",
                  "default_color": "#2563eb"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/cashbook-entries": {
      "parameters": [
        {
          "$ref": "#/components/parameters/APIVersion"
        }
      ],
      "get": {
        "tags": [
          "CashBook"
        ],
        "summary": "List cashbook entries",
        "x-summary-it": "Elenca i movimenti di cassa",
        "x-scope": "cashbook.read",
        "x-description-it": "I movimenti di prima nota, incassi e pagamenti insieme. Filtra per\nconto con `financial_account_id` e per periodo con `from` / `to`.\n\nOgni riga porta `running_balance`, il saldo progressivo dopo quel\nmovimento, e i riferimenti al documento che lo ha generato (fattura,\nscontrino, scheda lavoro o spesa).\n",
        "description": "Returns a paginated, filtered, ordered window of cashbook\nentries for the authenticated user's company.\n\nThe response envelope extends the standard `ListEnvelope`\nwith two cashbook-specific fields:\n\n- `opening_balance` — the running-balance anchor at the start\n  of the `from` window. Zero when no `from` is set (the\n  running balance simply accumulates from zero on the first\n  row).\n- `total_items` — the full filtered count, so clients can\n  render \"n of N\" without a follow-up request.\n\nPage rows carry `running_balance` already computed in\n`order_direction` from `opening_balance`. Do not re-sort\nclient-side without recomputing.\n",
        "parameters": [
          {
            "name": "tab",
            "in": "query",
            "required": false,
            "description": "Row-type filter. `tutto` (default) returns both income and\nexpense; `entrate` returns income only; `uscite` returns\nexpense only. The Italian tokens mirror the web app's URL\nparams so deep-links transfer 1:1.\n",
            "schema": {
              "type": "string",
              "enum": [
                "tutto",
                "entrate",
                "uscite"
              ],
              "default": "tutto"
            }
          },
          {
            "name": "financial_account_id",
            "in": "query",
            "required": false,
            "description": "Filter to a single financial account (UUID).",
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/DatePreset"
          },
          {
            "name": "from",
            "in": "query",
            "required": false,
            "description": "Inclusive lower bound on `payment_date` (YYYY-MM-DD).\nWhen set, `opening_balance` in the response carries the\nrunning total of all earlier matching rows; the page\nitself starts from zero offset within the window.\n",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "to",
            "in": "query",
            "required": false,
            "description": "Inclusive upper bound on `payment_date` (YYYY-MM-DD).",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "q",
            "in": "query",
            "required": false,
            "description": "Substring match on the counterparty name (`customer_name`\nfor income rows, `supplier_name` for expense rows).\n",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Page size. Default 20, max 100. Out-of-range values clamp\nsilently — never error.\n",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 20
            }
          },
          {
            "name": "starting_after",
            "in": "query",
            "required": false,
            "description": "Opaque cursor from a prior response's next-page link.\nPass verbatim. Omit on the first page.\n",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "order_direction",
            "in": "query",
            "required": false,
            "description": "Sort by `payment_date`. `desc` (default) lists\nnewest-first; `asc` lists oldest-first. Invalid values\nare rejected with 422 — silent clamping would silently\nflip the running balance.\n",
            "schema": {
              "type": "string",
              "enum": [
                "asc",
                "desc"
              ],
              "default": "desc"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Page of cashbook entries.",
            "x-description-it": "I movimenti di prima nota.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CashBookListEnvelope"
                },
                "example": {
                  "object": "list",
                  "data": [
                    {
                      "object": "cashbook_entry",
                      "id": "2f8b6d31-7a05-4c94-8e26-1d5f3a9c7b40",
                      "payment_type": "incasso",
                      "amount": 285.36,
                      "payment_date": "2026-07-17",
                      "financial_account_id": "d61c8b04-3f97-4a25-8e70-9b2d5f1a6c38",
                      "financial_account_name": "Cassa",
                      "customer_name": "Mario Rossi",
                      "supplier_name": null,
                      "invoice_id": "4412e7b9-0c35-4a18-92d7-5b6ea1f38c04",
                      "invoice_seq_number": 214,
                      "invoice_issued_at": "2026-07-16T10:30:00Z",
                      "running_balance": 4821.9
                    }
                  ],
                  "has_more": false,
                  "url": "/v1/cashbook-entries",
                  "date_filter": {
                    "object": "date_filter",
                    "preset": "year",
                    "from": "2026-01-01",
                    "to": "2026-12-31"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMediaType"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/checkins": {
      "parameters": [
        {
          "$ref": "#/components/parameters/APIVersion"
        }
      ],
      "get": {
        "tags": [
          "Checkins"
        ],
        "summary": "List checkins",
        "x-summary-it": "Elenca le accettazioni",
        "x-scope": "checkins.read",
        "x-description-it": "Le accettazioni dell'officina, dalla più recente. Filtra per stato con\n`status` (`active` o `completed`), per testo con `q` e per periodo con\n`from` / `to`. Negli elenchi `line_items` è `null` per alleggerire la\nrisposta: le righe arrivano solo sul dettaglio.\n",
        "description": "Returns checkins in the authenticated user's company, newest\nfirst. Filter by status with `?status=active|completed`; search\nby customer name with `?q=`; bound by `created_at` with\n`?from=` / `?to=` / `?preset=`; paginate with `?limit=` +\n`?starting_after=`.\n\nThe date window defaults to the last 12 months; set it with\n`from`/`to`/`preset`. The applied range is echoed in\n`date_filter`. Pass `?preset=all`\nto list across all time.\n\nThe cursor is opaque — clients MUST NOT parse it. Pass back the\nvalue verbatim. `has_more` is `true` when more results exist\nbeyond this page.\n\nList responses omit `line_items` to keep payloads small. Fetch\nthe detail endpoint when you need the full shape.\n",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": false,
            "description": "Substring match on the linked customer's full name.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "description": "Filter by lifecycle state. Omit to return both.",
            "schema": {
              "type": "string",
              "enum": [
                "active",
                "completed"
              ]
            }
          },
          {
            "name": "from",
            "in": "query",
            "required": false,
            "description": "Inclusive lower bound on `created_at` (YYYY-MM-DD).",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "to",
            "in": "query",
            "required": false,
            "description": "Inclusive upper bound on `created_at` (YYYY-MM-DD).",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "$ref": "#/components/parameters/DatePreset"
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Page size. Default 20, max 100. Out-of-range values clamp\nsilently to the defaults — never error.\n",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 20
            }
          },
          {
            "name": "starting_after",
            "in": "query",
            "required": false,
            "description": "Opaque cursor from a prior response. Pass verbatim. Omit on\nthe first page.\n",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "direction",
            "in": "query",
            "required": false,
            "description": "Direzione di ordinamento: `asc` o `desc`.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "sort",
            "in": "query",
            "required": false,
            "description": "Colonna di ordinamento.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Page of checkins.",
            "x-description-it": "Una pagina di accettazioni.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ListEnvelope"
                    },
                    {
                      "type": "object",
                      "required": [
                        "date_filter"
                      ],
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/Checkin"
                          }
                        },
                        "date_filter": {
                          "$ref": "#/components/schemas/DateFilter"
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "object": "list",
                  "data": [
                    {
                      "object": "checkin",
                      "id": "e4b7d290-6c15-4a83-9f27-1d8b3e5a7c60",
                      "seq_number": 142,
                      "status": "active",
                      "title": "Accettazione tagliando",
                      "customer_id": "4c911f08-7713-4e83-bdf6-a0692a1b43fd",
                      "customer_name": "Mario Rossi",
                      "vehicle_id": "8821f0ac-5d2b-41e7-9a10-6c3f2b7d4e55",
                      "vehicle_name": "Fiat Panda",
                      "vehicle_license": "AB123CD",
                      "work_category_id": "3a8e5c21-9d76-4f03-b418-2c6a9e7d5b40",
                      "work_category_name": "Tagliando",
                      "km": 61240,
                      "fuel_level": 45,
                      "worksheet_id": null,
                      "quote_id": null,
                      "created_at": "2026-07-15T07:40:00Z",
                      "updated_at": "2026-07-15T07:55:00Z"
                    }
                  ],
                  "has_more": true,
                  "next_starting_after": "eyJvIjoyMH0",
                  "url": "/v1/checkins",
                  "date_filter": {
                    "object": "date_filter",
                    "preset": "year",
                    "from": "2026-01-01",
                    "to": "2026-12-31"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMediaType"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      },
      "post": {
        "tags": [
          "Checkins"
        ],
        "summary": "Create a checkin",
        "x-summary-it": "Crea un'accettazione",
        "x-scope": "checkins.write",
        "x-description-it": "Registra l'accettazione di un veicolo. Il numero progressivo\n(`seq_number`) è assegnato dal server e resta stabile.\n\nI campi da `section1` a `section8` sono caselle di testo libero usate\nper le note di accettazione; l'API non impone loro alcun significato.\n`fuel_level` è un intero, per convenzione 0-100, senza vincoli.\n",
        "description": "Creates an intake/inspection record for an existing vehicle.\n\nRequired body fields: `customer_id`, `vehicle_id`. Everything\nelse is optional. The server assigns `id`, `company_id` (from\nthe authenticated user), `created_by`, `seq_number`, timestamps,\nand an initial `status` of `\"active\"`.\n\nLine-item creation is out of scope for v1 — a created checkin\nstarts with an empty `line_items` array. The response is the\nfully materialized post-create entity (same shape as\n`GET /v1/checkins/{id}`).\n\nForeign-key references in the body (`customer_id`, `vehicle_id`,\n`work_category_id`) must resolve to resources owned by the\nauthenticated user's company. Unreachable refs return 422\n`parameter_invalid` pointing at the offending field.\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CheckinCreateRequest"
              },
              "example": {
                "customer_id": "4c911f08-7713-4e83-bdf6-a0692a1b43fd",
                "vehicle_id": "9181c4de-2a67-4b93-8f01-1e7d90a3c6b2",
                "title": "Intake — annual service",
                "fuel_level": 50,
                "km": 124301.5,
                "section1": "Front bumper has a scuff on the left side."
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Checkin created.",
            "x-description-it": "L'accettazione creata.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Checkin"
                },
                "example": {
                  "object": "checkin",
                  "id": "e4b7d290-6c15-4a83-9f27-1d8b3e5a7c60",
                  "seq_number": 142,
                  "status": "active",
                  "title": "Accettazione tagliando",
                  "customer_id": "4c911f08-7713-4e83-bdf6-a0692a1b43fd",
                  "customer_name": "Mario Rossi",
                  "vehicle_id": "8821f0ac-5d2b-41e7-9a10-6c3f2b7d4e55",
                  "vehicle_name": "Fiat Panda",
                  "vehicle_license": "AB123CD",
                  "work_category_id": "3a8e5c21-9d76-4f03-b418-2c6a9e7d5b40",
                  "work_category_name": "Tagliando",
                  "km": 61240,
                  "fuel_level": 45,
                  "worksheet_id": null,
                  "quote_id": null,
                  "created_at": "2026-07-15T07:40:00Z",
                  "updated_at": "2026-07-15T07:55:00Z"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/MalformedRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMediaType"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/checkins/status-counts": {
      "parameters": [
        {
          "$ref": "#/components/parameters/APIVersion"
        }
      ],
      "get": {
        "tags": [
          "Checkins"
        ],
        "summary": "Checkin status counts",
        "x-summary-it": "Conta le accettazioni per stato",
        "x-scope": "checkins.read",
        "x-description-it": "Quante accettazioni sono `active` e quante `completed`, con gli\nstessi filtri dell'elenco. Serve a popolare i contatori senza\nscaricare le accettazioni.\n",
        "operationId": "checkinStatusCounts",
        "description": "Returns how many accettazioni sit in each state, for the tab counters\non the checkins list (web checkin-list StatusCount parity). Accepts\nthe same filters as the list (minus status/pagination) so the numbers\nalways match what the list shows:\n\nq                   optional substring match on customer/vehicle/plate\nfrom, to, preset    date filter — same semantics as\nGET /v1/checkins; the applied range is echoed\nback in `date_filter`\n",
        "parameters": [
          {
            "name": "from",
            "in": "query",
            "required": false,
            "description": "Inizio dell'intervallo, formato `YYYY-MM-DD`.",
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/DatePreset"
          },
          {
            "name": "q",
            "in": "query",
            "required": false,
            "description": "Ricerca testuale libera.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "to",
            "in": "query",
            "required": false,
            "description": "Fine dell'intervallo, formato `YYYY-MM-DD`.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "success",
            "x-description-it": "Il numero di accettazioni per stato.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CheckinStatusCounts"
                },
                "example": {
                  "object": "checkin_status_counts",
                  "active": 4,
                  "completed": 87,
                  "date_filter": {
                    "object": "date_filter",
                    "preset": "year",
                    "from": "2026-01-01",
                    "to": "2026-12-31"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/checkins/{id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/APIVersion"
        },
        {
          "name": "id",
          "in": "path",
          "required": true,
          "description": "Checkin id (UUID).",
          "schema": {
            "type": "string"
          }
        }
      ],
      "get": {
        "tags": [
          "Checkins"
        ],
        "summary": "Retrieve a checkin",
        "x-summary-it": "Recupera un'accettazione",
        "x-scope": "checkins.read",
        "x-description-it": "Restituisce l'accettazione con le righe (`line_items`). Se è già\nstata convertita, `worksheet_id` e `quote_id` puntano ai documenti\ngenerati.\n",
        "description": "Returns a single checkin by id, scoped to the authenticated\nuser's company. Unknown ids, ids from another company, and\nsoft-deleted rows all return 404 — never 403, never 200 with\ndeleted_at populated.\n\nDetail responses include `line_items`; list responses omit them.\n",
        "responses": {
          "200": {
            "description": "Checkin found.",
            "x-description-it": "L'accettazione richiesta.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Checkin"
                },
                "example": {
                  "object": "checkin",
                  "id": "e4b7d290-6c15-4a83-9f27-1d8b3e5a7c60",
                  "seq_number": 142,
                  "status": "active",
                  "title": "Accettazione tagliando",
                  "customer_id": "4c911f08-7713-4e83-bdf6-a0692a1b43fd",
                  "customer_name": "Mario Rossi",
                  "vehicle_id": "8821f0ac-5d2b-41e7-9a10-6c3f2b7d4e55",
                  "vehicle_name": "Fiat Panda",
                  "vehicle_license": "AB123CD",
                  "work_category_id": "3a8e5c21-9d76-4f03-b418-2c6a9e7d5b40",
                  "work_category_name": "Tagliando",
                  "km": 61240,
                  "fuel_level": 45,
                  "worksheet_id": null,
                  "quote_id": null,
                  "created_at": "2026-07-15T07:40:00Z",
                  "updated_at": "2026-07-15T07:55:00Z",
                  "comments": "Cliente segnala rumore in frenata.",
                  "section1": "Carrozzeria senza danni evidenti.",
                  "section2": "Pneumatici anteriori da sostituire.",
                  "line_items": [
                    {
                      "object": "checkin_line_item",
                      "id": "5c1e8b73-2a94-4d06-b8f5-3e7a1d4c9601",
                      "description": "Controllare pastiglie anteriori",
                      "created_at": "2026-07-15T07:52:00Z"
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMediaType"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      },
      "patch": {
        "tags": [
          "Checkins"
        ],
        "summary": "Update a checkin",
        "x-summary-it": "Aggiorna un'accettazione",
        "x-scope": "checkins.write",
        "x-description-it": "Aggiorna solo i campi presenti nel corpo: gli altri restano\ninvariati, e `null` svuota il campo. Lo stato non si cambia da qui:\nusa `POST /v1/checkins/{id}/transitions`.\n",
        "description": "Three-state PATCH semantics (omitted = unchanged, `null` =\nclear, value = set).\n\nFields NOT patchable here: `status` (use the transitions\nendpoint), `customer_id`, `vehicle_id` (immutable),\n`seq_number`, `created_by`, timestamps, `line_items`.\n\nReturns the fully materialized post-update checkin.\n\nForeign-key references in the body (`work_category_id`,\n`worksheet_id`, `quote_id`) must resolve to resources owned by\nthe authenticated user's company. Unreachable refs return 422\n`parameter_invalid` pointing at the offending field.\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CheckinPatchRequest"
              },
              "example": {
                "title": "Intake — updated note",
                "fuel_level": 75,
                "section1": null,
                "km": 124350
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Checkin updated.",
            "x-description-it": "L'accettazione aggiornata.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Checkin"
                },
                "example": {
                  "object": "checkin",
                  "id": "e4b7d290-6c15-4a83-9f27-1d8b3e5a7c60",
                  "seq_number": 142,
                  "status": "active",
                  "title": "Accettazione tagliando",
                  "customer_id": "4c911f08-7713-4e83-bdf6-a0692a1b43fd",
                  "customer_name": "Mario Rossi",
                  "vehicle_id": "8821f0ac-5d2b-41e7-9a10-6c3f2b7d4e55",
                  "vehicle_name": "Fiat Panda",
                  "vehicle_license": "AB123CD",
                  "work_category_id": "3a8e5c21-9d76-4f03-b418-2c6a9e7d5b40",
                  "work_category_name": "Tagliando",
                  "km": 61240,
                  "fuel_level": 45,
                  "worksheet_id": null,
                  "quote_id": null,
                  "created_at": "2026-07-15T07:40:00Z",
                  "updated_at": "2026-07-15T07:55:00Z",
                  "comments": "Cliente segnala rumore in frenata.",
                  "section1": "Carrozzeria senza danni evidenti.",
                  "section2": "Pneumatici anteriori da sostituire.",
                  "line_items": [
                    {
                      "object": "checkin_line_item",
                      "id": "5c1e8b73-2a94-4d06-b8f5-3e7a1d4c9601",
                      "description": "Controllare pastiglie anteriori",
                      "created_at": "2026-07-15T07:52:00Z"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/MalformedRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMediaType"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      },
      "delete": {
        "tags": [
          "Checkins"
        ],
        "summary": "Soft-delete a checkin",
        "x-summary-it": "Elimina un'accettazione",
        "x-scope": "checkins.write",
        "x-description-it": "Eliminazione logica: l'accettazione sparisce dagli elenchi. I\ndocumenti eventualmente generati da essa restano intatti.\n",
        "description": "Stamps `deleted_at`; subsequent GET/PATCH/DELETE all 404.\nReturns the deleted resource (200) for consistency.\n",
        "responses": {
          "200": {
            "description": "Checkin soft-deleted.",
            "x-description-it": "L'accettazione eliminata.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Checkin"
                },
                "example": {
                  "object": "checkin",
                  "id": "e4b7d290-6c15-4a83-9f27-1d8b3e5a7c60",
                  "seq_number": 142,
                  "status": "active",
                  "title": "Accettazione tagliando",
                  "customer_id": "4c911f08-7713-4e83-bdf6-a0692a1b43fd",
                  "customer_name": "Mario Rossi",
                  "vehicle_id": "8821f0ac-5d2b-41e7-9a10-6c3f2b7d4e55",
                  "vehicle_name": "Fiat Panda",
                  "vehicle_license": "AB123CD",
                  "work_category_id": "3a8e5c21-9d76-4f03-b418-2c6a9e7d5b40",
                  "work_category_name": "Tagliando",
                  "km": 61240,
                  "fuel_level": 45,
                  "worksheet_id": null,
                  "quote_id": null,
                  "created_at": "2026-07-15T07:40:00Z",
                  "updated_at": "2026-07-15T07:55:00Z",
                  "deleted_at": "2026-07-20T11:04:00Z"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/checkins/{id}/convert": {
      "parameters": [
        {
          "$ref": "#/components/parameters/APIVersion"
        },
        {
          "name": "id",
          "in": "path",
          "required": true,
          "description": "Checkin id (UUID).",
          "schema": {
            "type": "string"
          }
        }
      ],
      "post": {
        "tags": [
          "Checkins"
        ],
        "summary": "Convert into a scheda lavoro",
        "x-summary-it": "Converti in scheda lavoro",
        "x-scope": "checkins.write",
        "x-description-it": "Genera una scheda lavoro dall'accettazione e collega i due documenti\n(`worksheet_id` sull'accettazione).\n\nAttenzione: la risposta è la **scheda lavoro** creata, non\nl'accettazione — l'oggetto restituito ha `object: worksheet`.\n",
        "description": "Creates a worksheet carrying the checkin's customer, vehicle, km,\ntitle, notes, work category and delivery date — the same conversion\nthe web performs (POST /hx/private/worksheets/checkins/{id}) — then\nlinks the checkin to it (status → `completed`, `worksheet_id` set).\nThe checkin is kept. A checkin already linked to a worksheet returns\n422; when the company requires km on vehicle-linked worksheets and\nthe checkin has none, 422 `parameter_missing` `/km`.\n\nNo request body.\n",
        "responses": {
          "201": {
            "description": "The freshly created scheda lavoro (full detail shape).",
            "x-description-it": "La scheda lavoro generata.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Worksheet"
                },
                "example": {
                  "object": "worksheet",
                  "id": "a5d21e64-8f37-4b90-ae12-3c7d6b048f95",
                  "seq_number": 412,
                  "status": "draft",
                  "title": "Accettazione tagliando",
                  "customer_id": "4c911f08-7713-4e83-bdf6-a0692a1b43fd",
                  "vehicle_id": "8821f0ac-5d2b-41e7-9a10-6c3f2b7d4e55",
                  "total_after_tax": 0,
                  "frozen": false,
                  "created_at": "2026-07-15T08:12:00Z"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMediaType"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/checkins/{id}/documents": {
      "parameters": [
        {
          "$ref": "#/components/parameters/APIVersion"
        },
        {
          "name": "id",
          "in": "path",
          "required": true,
          "description": "Identifier of the target resource.",
          "schema": {
            "type": "string"
          }
        }
      ],
      "post": {
        "tags": [
          "Checkins"
        ],
        "summary": "Create a checkin document",
        "x-summary-it": "Allega un documento all'accettazione",
        "x-scope": "checkins.write",
        "x-description-it": "Carica un file fra i documenti dell'accettazione. La richiesta è\n`multipart/form-data` con il file nel campo `file`, non JSON.\n",
        "operationId": "checkinDocumentCreate",
        "description": "multipart/form-data:\n\nfile         required — the document bytes (max 25MB)\ndescription  optional — display label; defaults to the file name\n",
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "required": [
                  "file"
                ],
                "properties": {
                  "file": {
                    "type": "string",
                    "format": "binary"
                  },
                  "description": {
                    "type": "string",
                    "description": "Display label; defaults to the file name."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "success",
            "x-description-it": "Il documento allegato.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CheckinDocument"
                },
                "example": {
                  "object": "checkin_document",
                  "id": "7f2c6a18-9e40-4b53-8d21-4a6f1c9e3b75",
                  "name": "foto-danno.jpg",
                  "content_type": "image/jpeg",
                  "description": "Graffio parafango",
                  "file_size_bytes": 233104,
                  "url": "/v1/checkins/e4b7d290-6c15-4a83-9f27-1d8b3e5a7c60/documents/7f2c6a18-9e40-4b53-8d21-4a6f1c9e3b75/download",
                  "created_at": "2026-07-15T07:58:00Z"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/checkins/{id}/documents/{documentID}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/APIVersion"
        },
        {
          "name": "id",
          "in": "path",
          "required": true,
          "description": "Identifier of the target resource.",
          "schema": {
            "type": "string"
          }
        },
        {
          "name": "documentID",
          "in": "path",
          "required": true,
          "description": "Identifier of the target resource.",
          "schema": {
            "type": "string"
          }
        }
      ],
      "delete": {
        "tags": [
          "Checkins"
        ],
        "summary": "Delete checkin document",
        "x-summary-it": "Elimina un documento",
        "x-scope": "checkins.write",
        "x-description-it": "Rimuove definitivamente il file allegato.\n",
        "operationId": "checkinDocumentDelete",
        "description": "CheckinDocumentDelete handles\nDELETE /v1/checkins/{id}/documents/{documentID}.\n",
        "responses": {
          "200": {
            "description": "success (the removed document)",
            "x-description-it": "Il documento eliminato.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CheckinDocument"
                },
                "example": {
                  "object": "checkin_document",
                  "id": "7f2c6a18-9e40-4b53-8d21-4a6f1c9e3b75",
                  "name": "foto-danno.jpg",
                  "content_type": "image/jpeg",
                  "description": "Graffio parafango",
                  "file_size_bytes": 233104,
                  "url": "/v1/checkins/e4b7d290-6c15-4a83-9f27-1d8b3e5a7c60/documents/7f2c6a18-9e40-4b53-8d21-4a6f1c9e3b75/download",
                  "created_at": "2026-07-15T07:58:00Z"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/checkins/{id}/documents/{documentID}/download": {
      "parameters": [
        {
          "$ref": "#/components/parameters/APIVersion"
        },
        {
          "name": "id",
          "in": "path",
          "required": true,
          "description": "Identifier of the target resource.",
          "schema": {
            "type": "string"
          }
        },
        {
          "name": "documentID",
          "in": "path",
          "required": true,
          "description": "Identifier of the target resource.",
          "schema": {
            "type": "string"
          }
        }
      ],
      "get": {
        "tags": [
          "Checkins"
        ],
        "summary": "Download checkin document",
        "x-summary-it": "Scarica un documento",
        "x-scope": "checkins.read",
        "x-description-it": "Restituisce il contenuto del file, non JSON. È il percorso indicato\ndal campo `url` di ogni documento.\n",
        "operationId": "checkinDocumentDownload",
        "description": "CheckinDocumentDownload handles\nGET /v1/checkins/{id}/documents/{documentID}/download.\n",
        "responses": {
          "200": {
            "description": "success",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/checkins/{id}/line-items/batch": {
      "parameters": [
        {
          "$ref": "#/components/parameters/APIVersion"
        },
        {
          "name": "id",
          "in": "path",
          "required": true,
          "description": "Checkin id (UUID).",
          "schema": {
            "type": "string"
          }
        }
      ],
      "post": {
        "tags": [
          "Checkins"
        ],
        "summary": "Mutate checkin line items in a batch",
        "x-summary-it": "Modifica le righe in blocco",
        "x-scope": "checkins.write",
        "x-description-it": "Aggiunge, aggiorna, rimuove e riordina le righe in una sola\nrichiesta, applicata come singola transazione. Le righe di\nun'accettazione sono sole annotazioni: portano una `description` e\nnessun prezzo.\n",
        "description": "Adds, updates, and removes the checkin's line items in a single\natomic diff, then returns the full checkin with the updated\n`line_items` array. The top-level checkin PATCH is metadata-only;\nall line-item mutation flows through this endpoint.\n\nCheckin line items are description-only notes, so this batch is\nnarrower than the worksheet/quote one: the only field is\n`description`, and there is no `reorder` operation (the rows have\nno position). As with worksheets/quotes, `update` is applied as a\nremove+add internally, so an updated line receives a NEW id in the\nresponse. An id may appear in at most one of `remove`/`update`.\n\nAll arrays are optional; an empty body is a no-op.\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CheckinLineItemsBatchRequest"
              },
              "example": {
                "add": [
                  {
                    "description": "Scratch on rear bumper"
                  },
                  {
                    "description": "Spare tyre missing"
                  }
                ],
                "update": [
                  {
                    "id": "4b1d8f2a-0c3e-4a6b-9d7c-1e2f3a4b5c6d",
                    "description": "Windshield chip (driver side)"
                  }
                ],
                "remove": [
                  "9f8e7d6c-5b4a-3c2d-1e0f-a9b8c7d6e5f4"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Batch applied. Returns the updated checkin (with `line_items`).",
            "x-description-it": "L'accettazione con le righe aggiornate.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Checkin"
                },
                "example": {
                  "object": "checkin",
                  "id": "e4b7d290-6c15-4a83-9f27-1d8b3e5a7c60",
                  "seq_number": 142,
                  "status": "active",
                  "title": "Accettazione tagliando",
                  "customer_id": "4c911f08-7713-4e83-bdf6-a0692a1b43fd",
                  "customer_name": "Mario Rossi",
                  "vehicle_id": "8821f0ac-5d2b-41e7-9a10-6c3f2b7d4e55",
                  "vehicle_name": "Fiat Panda",
                  "vehicle_license": "AB123CD",
                  "work_category_id": "3a8e5c21-9d76-4f03-b418-2c6a9e7d5b40",
                  "work_category_name": "Tagliando",
                  "km": 61240,
                  "fuel_level": 45,
                  "worksheet_id": null,
                  "quote_id": null,
                  "created_at": "2026-07-15T07:40:00Z",
                  "updated_at": "2026-07-15T07:55:00Z",
                  "comments": "Cliente segnala rumore in frenata.",
                  "section1": "Carrozzeria senza danni evidenti.",
                  "section2": "Pneumatici anteriori da sostituire.",
                  "line_items": [
                    {
                      "object": "checkin_line_item",
                      "id": "5c1e8b73-2a94-4d06-b8f5-3e7a1d4c9601",
                      "description": "Controllare pastiglie anteriori",
                      "created_at": "2026-07-15T07:52:00Z"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/MalformedRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMediaType"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/checkins/{id}/quotes": {
      "parameters": [
        {
          "$ref": "#/components/parameters/APIVersion"
        },
        {
          "name": "id",
          "in": "path",
          "required": true,
          "description": "Checkin id (UUID).",
          "schema": {
            "type": "string"
          }
        }
      ],
      "post": {
        "tags": [
          "Checkins"
        ],
        "summary": "Create a preventivo from this accettazione",
        "x-summary-it": "Crea un preventivo dall'accettazione",
        "x-scope": "checkins.write",
        "x-description-it": "Genera un preventivo dall'accettazione e collega i due documenti\n(`quote_id` sull'accettazione).\n\nAttenzione: la risposta è il **preventivo** creato, non\nl'accettazione — l'oggetto restituito ha `object: quote`.\n",
        "description": "Creates a quote with the checkin's customer, vehicle, km, title,\nnotes, work category and delivery date. Each checkin line item\n(inconveniente) becomes a quote line at qty 1 pz with the company's\nfirst enabled VAT rate — the same conversion the web performs\n(POST /hx/private/quotes/checkins/{id}). Links the checkin\n(status → `completed`, `quote_id` set). The checkin is kept.\nAlready linked → 422.\n\nNo request body.\n",
        "responses": {
          "201": {
            "description": "The freshly created preventivo (full detail shape).",
            "x-description-it": "Il preventivo generato.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Quote"
                },
                "example": {
                  "object": "quote",
                  "id": "b93f7c25-4a18-4e60-9d37-8c1b5f2a6e04",
                  "seq_number": 87,
                  "status": "waiting",
                  "title": "Accettazione tagliando",
                  "customer_id": "4c911f08-7713-4e83-bdf6-a0692a1b43fd",
                  "vehicle_id": "8821f0ac-5d2b-41e7-9a10-6c3f2b7d4e55",
                  "total_after_tax": 0,
                  "created_at": "2026-07-15T08:15:00Z"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMediaType"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/checkins/{id}/transitions": {
      "parameters": [
        {
          "$ref": "#/components/parameters/APIVersion"
        },
        {
          "name": "id",
          "in": "path",
          "required": true,
          "description": "Checkin id (UUID).",
          "schema": {
            "type": "string"
          }
        }
      ],
      "post": {
        "tags": [
          "Checkins"
        ],
        "summary": "Transition a checkin's status",
        "x-summary-it": "Cambia stato all'accettazione",
        "x-scope": "checkins.write",
        "x-description-it": "Porta l'accettazione in `active` o `completed`. È l'unico modo di\ncambiare `status`: un valore diverso da questi due viene rifiutato con\n`422`.\n",
        "description": "Drives the lifecycle state machine. The body is\n`{\"action\": \"active\" | \"completed\"}`. Any other action value\nis a 422 `parameter_invalid`.\n\nStatus transitions live behind their own verb so the server\ncan stamp lifecycle timestamps and run side effects centrally\n— flipping `status` through PATCH is intentionally not\nsupported.\n\nReturns the fully materialized post-transition entity.\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CheckinTransitionRequest"
              },
              "example": {
                "action": "completed"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Transition applied.",
            "x-description-it": "L'accettazione dopo la transizione.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Checkin"
                },
                "example": {
                  "object": "checkin",
                  "id": "e4b7d290-6c15-4a83-9f27-1d8b3e5a7c60",
                  "seq_number": 142,
                  "status": "completed",
                  "title": "Accettazione tagliando",
                  "customer_id": "4c911f08-7713-4e83-bdf6-a0692a1b43fd",
                  "customer_name": "Mario Rossi",
                  "vehicle_id": "8821f0ac-5d2b-41e7-9a10-6c3f2b7d4e55",
                  "vehicle_name": "Fiat Panda",
                  "vehicle_license": "AB123CD",
                  "work_category_id": "3a8e5c21-9d76-4f03-b418-2c6a9e7d5b40",
                  "work_category_name": "Tagliando",
                  "km": 61240,
                  "fuel_level": 45,
                  "worksheet_id": null,
                  "quote_id": null,
                  "created_at": "2026-07-15T07:40:00Z",
                  "updated_at": "2026-07-15T07:55:00Z",
                  "comments": "Cliente segnala rumore in frenata.",
                  "section1": "Carrozzeria senza danni evidenti.",
                  "section2": "Pneumatici anteriori da sostituire.",
                  "line_items": [
                    {
                      "object": "checkin_line_item",
                      "id": "5c1e8b73-2a94-4d06-b8f5-3e7a1d4c9601",
                      "description": "Controllare pastiglie anteriori",
                      "created_at": "2026-07-15T07:52:00Z"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/MalformedRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMediaType"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/company": {
      "parameters": [
        {
          "$ref": "#/components/parameters/APIVersion"
        }
      ],
      "get": {
        "tags": [
          "Settings"
        ],
        "summary": "Retrieve a company",
        "x-summary-it": "Recupera i dati azienda",
        "x-scope": "settings.read",
        "x-description-it": "L'anagrafica dell'officina e le preferenze che governano i documenti:\nmetodo di pagamento predefinito, colore, IBAN, se i prezzi sono IVA\ninclusa.\n\n`fiscal_fields_locked` indica che partita IVA e codice fiscale non\nsono più modificabili perché sono già stati usati su un documento\nfiscale.\n",
        "operationId": "companyGet",
        "responses": {
          "200": {
            "description": "success",
            "x-description-it": "I dati dell'azienda.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/json": {
                "example": {
                  "object": "company_profile",
                  "id": "1f8c4e73-2a95-4b06-9d31-7e5a2c8b6f40",
                  "name": "Officina Rossi S.r.l.",
                  "entity_type": "societa",
                  "company_type": "officina",
                  "tax_id": "01234567890",
                  "fiscal_code": "RSSMRA80A01F205X",
                  "email": "info@officinarossi.example.it",
                  "phone": "+390212345678",
                  "address": "Via Mecenate 12",
                  "city": "Milano",
                  "zip_code": "20138",
                  "province": "MI",
                  "fiscal_fields_locked": true,
                  "primary_color": "#2563eb",
                  "default_payment_method": "MP05",
                  "bank_account_holder": "Officina Rossi S.r.l.",
                  "line_items_including_vat": false,
                  "worksheet_km_required": true
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/credit-notes": {
      "parameters": [
        {
          "$ref": "#/components/parameters/APIVersion"
        }
      ],
      "get": {
        "tags": [
          "CreditNotes"
        ],
        "summary": "List credit notes",
        "x-summary-it": "Elenca le note di credito",
        "x-scope": "invoices.read",
        "x-description-it": "Le note di credito emesse, dalla più recente. Stessa paginazione a\ncursore dell'elenco fatture.\n",
        "description": "Returns credit notes in the authenticated user's company,\nnewest first. Search with `?q=` (substring against customer /\ntitle), filter by SDI lifecycle status with `?sdi_status=`,\nand bound by `created_at` with `?from=` / `?to=` / `?preset=`.\nPaginate with cursor-based `?limit=` + `?starting_after=`.\n\nThe date window defaults to the last 12 months; set it with\n`from`/`to`/`preset`. The applied range is echoed in\n`date_filter`.\n\nThe cursor is opaque — pass it back verbatim.\n",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": false,
            "description": "Substring match on customer / title.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "sdi_status",
            "in": "query",
            "required": false,
            "description": "Filter by SDI lifecycle status.",
            "schema": {
              "type": "string",
              "enum": [
                "to_send",
                "sent",
                "delivered",
                "rejected"
              ]
            }
          },
          {
            "name": "from",
            "in": "query",
            "required": false,
            "description": "Inclusive lower bound on `created_at` (YYYY-MM-DD).",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "to",
            "in": "query",
            "required": false,
            "description": "Inclusive upper bound on `created_at` (YYYY-MM-DD).",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "$ref": "#/components/parameters/DatePreset"
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Page size. Default 20, max 100.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 20
            }
          },
          {
            "name": "starting_after",
            "in": "query",
            "required": false,
            "description": "Opaque cursor from a prior response.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Page of credit notes.",
            "x-description-it": "Una pagina di note di credito.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ListEnvelope"
                    },
                    {
                      "type": "object",
                      "required": [
                        "date_filter"
                      ],
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/CreditNote"
                          }
                        },
                        "date_filter": {
                          "$ref": "#/components/schemas/DateFilter"
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "object": "list",
                  "data": [
                    {
                      "object": "credit_note",
                      "id": "9e5a3d78-1b46-4c02-8f93-7a2d5e1c8b40",
                      "invoice_id": "4412e7b9-0c35-4a18-92d7-5b6ea1f38c04",
                      "seq_number": 12,
                      "title": "Storno parziale tagliando",
                      "sezionale_id": "c81f4a26-7d09-4e35-b6a1-2f8c5d3e9047",
                      "sezionale_code": "A",
                      "customer_name": "Mario Rossi",
                      "vehicle_name": "Fiat Panda",
                      "vehicle_license": "AB123CD",
                      "sdi_status": "delivered",
                      "total_before_tax": 74.0,
                      "total_tax": 16.28,
                      "total_after_tax": 90.28,
                      "created_at": "2026-07-18T09:00:00Z",
                      "updated_at": "2026-07-18T09:00:00Z"
                    }
                  ],
                  "has_more": false,
                  "url": "/v1/credit-notes"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMediaType"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      },
      "post": {
        "tags": [
          "CreditNotes"
        ],
        "summary": "Create a credit note from an invoice",
        "x-summary-it": "Crea una nota di credito",
        "x-scope": "invoices.write",
        "x-description-it": "Storna una fattura emessa: passa `invoice_id` e righe e totali\nvengono ripresi dalla fattura di origine. È il modo corretto di\nannullare una fattura già emessa, che non è eliminabile.\n",
        "description": "A credit note is a derivative document — every credit note\nis created FROM an existing invoice. The body carries the\nsource `invoice_id` (required) plus optional `title` and\n`sezionale_id`. All other fields (totals, customer, vehicle,\nseq number, timestamps) are derived from the source invoice\nor assigned server-side.\n\nThe source invoice MUST belong to the authenticated user's\ncompany. Unknown / cross-tenant invoice_ids return 404.\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreditNoteCreateRequest"
              },
              "example": {
                "invoice_id": "4412e7b9-0c35-4a18-92d7-5b6ea1f38c04",
                "title": "Storno parziale tagliando"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Credit note created.",
            "x-description-it": "La nota di credito creata.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreditNote"
                },
                "example": {
                  "object": "credit_note",
                  "id": "9e5a3d78-1b46-4c02-8f93-7a2d5e1c8b40",
                  "invoice_id": "4412e7b9-0c35-4a18-92d7-5b6ea1f38c04",
                  "seq_number": 12,
                  "title": "Storno parziale tagliando",
                  "sezionale_id": "c81f4a26-7d09-4e35-b6a1-2f8c5d3e9047",
                  "sezionale_code": "A",
                  "customer_name": "Mario Rossi",
                  "vehicle_name": "Fiat Panda",
                  "vehicle_license": "AB123CD",
                  "sdi_status": "delivered",
                  "total_before_tax": 74.0,
                  "total_tax": 16.28,
                  "total_after_tax": 90.28,
                  "created_at": "2026-07-18T09:00:00Z",
                  "updated_at": "2026-07-18T09:00:00Z"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/MalformedRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMediaType"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/credit-notes/{id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/APIVersion"
        },
        {
          "name": "id",
          "in": "path",
          "required": true,
          "description": "Credit note id (UUID).",
          "schema": {
            "type": "string"
          }
        }
      ],
      "get": {
        "tags": [
          "CreditNotes"
        ],
        "summary": "Retrieve a credit note",
        "x-summary-it": "Recupera una nota di credito",
        "x-scope": "invoices.read",
        "x-description-it": "Restituisce la nota di credito e il riferimento alla fattura\nstornata (`invoice_id`).\n",
        "description": "Returns the credit note if it belongs to the authenticated\nuser's company. Unknown ids, ids from another company, and\nsoft-deleted rows all return 404 — never 403.\n",
        "responses": {
          "200": {
            "description": "Credit note found.",
            "x-description-it": "La nota di credito richiesta.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreditNote"
                },
                "example": {
                  "object": "credit_note",
                  "id": "9e5a3d78-1b46-4c02-8f93-7a2d5e1c8b40",
                  "invoice_id": "4412e7b9-0c35-4a18-92d7-5b6ea1f38c04",
                  "seq_number": 12,
                  "title": "Storno parziale tagliando",
                  "sezionale_id": "c81f4a26-7d09-4e35-b6a1-2f8c5d3e9047",
                  "sezionale_code": "A",
                  "customer_name": "Mario Rossi",
                  "vehicle_name": "Fiat Panda",
                  "vehicle_license": "AB123CD",
                  "sdi_status": "delivered",
                  "total_before_tax": 74.0,
                  "total_tax": 16.28,
                  "total_after_tax": 90.28,
                  "created_at": "2026-07-18T09:00:00Z",
                  "updated_at": "2026-07-18T09:00:00Z"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMediaType"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      },
      "patch": {
        "tags": [
          "CreditNotes"
        ],
        "summary": "Update a credit note's title",
        "x-summary-it": "Aggiorna una nota di credito",
        "x-scope": "invoices.write",
        "x-description-it": "Aggiorna i campi descrittivi. Gli importi derivano dalla fattura di\norigine e non si modificano da qui.\n",
        "description": "`title` is the only mutable field on a credit note from the\nmobile API. All other fields are either derived from the\nsource invoice, immutable identity, or server-managed.\n\nThree-state PATCH semantics:\n\n- Omitted → leave the entity field unchanged.\n- Explicit JSON `null` → clear the title (SQL NULL).\n- Concrete value → set the title.\n\nSending any other key returns 400 `malformed_request` —\nsurfaces typos and stale schema knowledge.\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreditNotePatchRequest"
              },
              "example": {
                "title": "Storno tagliando — corretto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Credit note updated.",
            "x-description-it": "La nota di credito aggiornata.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreditNote"
                },
                "example": {
                  "object": "credit_note",
                  "id": "9e5a3d78-1b46-4c02-8f93-7a2d5e1c8b40",
                  "invoice_id": "4412e7b9-0c35-4a18-92d7-5b6ea1f38c04",
                  "seq_number": 12,
                  "title": "Storno parziale tagliando",
                  "sezionale_id": "c81f4a26-7d09-4e35-b6a1-2f8c5d3e9047",
                  "sezionale_code": "A",
                  "customer_name": "Mario Rossi",
                  "vehicle_name": "Fiat Panda",
                  "vehicle_license": "AB123CD",
                  "sdi_status": "delivered",
                  "total_before_tax": 74.0,
                  "total_tax": 16.28,
                  "total_after_tax": 90.28,
                  "created_at": "2026-07-18T09:00:00Z",
                  "updated_at": "2026-07-18T09:00:00Z"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/MalformedRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMediaType"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      },
      "delete": {
        "tags": [
          "CreditNotes"
        ],
        "summary": "Soft-delete a credit note",
        "x-summary-it": "Elimina una nota di credito",
        "x-scope": "invoices.write",
        "x-description-it": "Eliminazione logica della nota di credito.\n\nUna nota di credito la cui controparte elettronica è già stata\ntrasmessa allo SdI non è eliminabile: la richiesta risponde\n`409`. Il documento è ormai un record fiscale e va gestito\ndal gestionale web.\n",
        "description": "Stamps `deleted_at`; the row stays in the database, and\nsubsequent GET/PATCH/DELETE all 404.\n\nIf the credit note has an already-transmitted SDI electronic\ncounterpart, deletion would orphan the electronic document\nand is refused with 409 `conflict`.\n\nReturns the deleted resource (200) for shape consistency\nand so the client can render an undo banner.\n",
        "responses": {
          "200": {
            "description": "Credit note soft-deleted.",
            "x-description-it": "La nota di credito eliminata.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreditNote"
                },
                "example": {
                  "object": "credit_note",
                  "id": "9e5a3d78-1b46-4c02-8f93-7a2d5e1c8b40",
                  "invoice_id": "4412e7b9-0c35-4a18-92d7-5b6ea1f38c04",
                  "seq_number": 12,
                  "title": "Storno parziale tagliando",
                  "sezionale_id": "c81f4a26-7d09-4e35-b6a1-2f8c5d3e9047",
                  "sezionale_code": "A",
                  "customer_name": "Mario Rossi",
                  "vehicle_name": "Fiat Panda",
                  "vehicle_license": "AB123CD",
                  "sdi_status": "delivered",
                  "total_before_tax": 74.0,
                  "total_tax": 16.28,
                  "total_after_tax": 90.28,
                  "created_at": "2026-07-18T09:00:00Z",
                  "updated_at": "2026-07-18T09:00:00Z",
                  "deleted_at": "2026-07-20T11:04:00Z"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/customers": {
      "parameters": [
        {
          "$ref": "#/components/parameters/APIVersion"
        }
      ],
      "get": {
        "tags": [
          "Customers"
        ],
        "summary": "List customers",
        "x-scope": "customers.read",
        "x-summary-it": "Elenca i clienti",
        "x-description-it": "Restituisce i clienti della tua officina, dal più recente.\nCerca per nome con `?q=`. Impagina con `?limit=` e `?starting_after=`.\n\nIl cursore è opaco: non interpretarlo, rimandalo così com'è.\nUsa `has_more` per sapere se c'è un'altra pagina, non la lunghezza di\n`data`: l'ultima pagina può essere piena.\n",
        "description": "Returns customers in the authenticated user's company, newest\nfirst. Search by name with `?q=`; paginate with cursor-based\n`?limit=` + `?starting_after=`.\n\nThe cursor is opaque — clients MUST NOT parse it. Pass back\nthe value verbatim. Today it encodes an offset; tomorrow it\nmay encode a `(created_at, id)` tuple without a client-visible\nchange.\n\n`has_more` is `true` when more results exist beyond this page.\nDrive the \"load more\" UI off this flag, not off `data.length`\n— the last page may still contain a full `limit` worth of\nresults.\n",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": false,
            "description": "Substring match on the customer's full name.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "sort",
            "in": "query",
            "required": false,
            "description": "Sort key (default `date` = creation date). Named keys are\nmapped server-side to whitelisted columns.\n",
            "schema": {
              "type": "string",
              "enum": [
                "date",
                "name",
                "vehicle",
                "spend"
              ]
            }
          },
          {
            "name": "direction",
            "in": "query",
            "required": false,
            "description": "Sort direction (default `desc`).",
            "schema": {
              "type": "string",
              "enum": [
                "asc",
                "desc"
              ]
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Page size. Default 20, max 100. Out-of-range values clamp\nsilently to the defaults — never error.\n",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 20
            }
          },
          {
            "name": "starting_after",
            "in": "query",
            "required": false,
            "description": "Opaque cursor from a prior response's next-page link. Pass\nverbatim. Omit on the first page.\n",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Page of customers.",
            "x-description-it": "Una pagina di clienti.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ListEnvelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/Customer"
                          }
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "object": "list",
                  "data": [
                    {
                      "object": "customer",
                      "id": "8f173fd2-6fa4-46e5-a8e3-23dfff31a0a1",
                      "customer_type": "private",
                      "full_name": "Mario Rossi",
                      "email": "mario.rossi@example.com",
                      "phone": "+393331234567",
                      "city": "Milano"
                    }
                  ],
                  "has_more": true,
                  "next_starting_after": "eyJvIjoyMH0",
                  "url": "/v1/customers"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMediaType"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      },
      "post": {
        "tags": [
          "Customers"
        ],
        "summary": "Create a customer",
        "x-scope": "customers.write",
        "x-summary-it": "Crea un cliente",
        "x-description-it": "Crea un cliente nella tua officina. L'azienda e l'autore sono sempre\nricavati dalla chiave API: non puoi impostarli dal corpo della richiesta.\n\nObbligatorio: `customer_type`. Tutto il resto è facoltativo.\n\nI riferimenti ad altre risorse (per esempio `price_list_id`) devono\nappartenere alla tua officina, altrimenti ricevi 422 `parameter_invalid`\ncon l'indicazione del campo.\n",
        "description": "Creates a customer in the authenticated user's company.\n`company_id` and `created_by` are always derived from the\nbearer token — neither can be set from the body.\n\nRequired: `customer_type`. Everything else is optional.\n\nReturns the fully materialized customer (re-fetched so\ncomputed fields like `last_vehicle_*` and `next_appointment`\npopulate consistently with GET).\n\nForeign-key references in the body (`price_list_id`, etc.) must\nresolve to resources owned by the authenticated user's company.\nUnreachable refs return 422 `parameter_invalid` pointing at the\noffending field.\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CustomerCreateRequest"
              },
              "example": {
                "customer_type": "private",
                "full_name": "Mario Rossi",
                "phone": "+39 333 1234567",
                "email": "mario.rossi@example.com"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Customer created.",
            "x-description-it": "Cliente creato.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Customer"
                },
                "example": {
                  "object": "customer",
                  "id": "8f173fd2-6fa4-46e5-a8e3-23dfff31a0a1",
                  "customer_type": "private",
                  "full_name": "Mario Rossi",
                  "email": "mario.rossi@example.com",
                  "phone": "+393331234567",
                  "city": "Milano",
                  "created_at": "2026-08-03T14:32:11Z"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/MalformedRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMediaType"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/customers/{id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/APIVersion"
        },
        {
          "name": "id",
          "in": "path",
          "required": true,
          "description": "Customer id (UUID).",
          "schema": {
            "type": "string"
          }
        }
      ],
      "get": {
        "tags": [
          "Customers"
        ],
        "summary": "Retrieve a customer",
        "x-scope": "customers.read",
        "x-summary-it": "Recupera un cliente",
        "x-description-it": "Restituisce un singolo cliente della tua officina.\n\nUn id sconosciuto, di un'altra officina o di un cliente eliminato\nrestituisce sempre 404: mai 403, mai 200 con il record eliminato.\n",
        "description": "Returns a single customer by id, scoped to the authenticated\nuser's company. Unknown ids, ids from another company, and\nsoft-deleted rows all return 404 — never 403, never 200 with\ndeleted_at populated. This prevents cross-tenant and\npost-deletion existence leaks.\n",
        "responses": {
          "200": {
            "description": "Customer found.",
            "x-description-it": "Cliente trovato.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Customer"
                },
                "example": {
                  "object": "customer",
                  "id": "8f173fd2-6fa4-46e5-a8e3-23dfff31a0a1",
                  "customer_type": "private",
                  "full_name": "Mario Rossi",
                  "email": "mario.rossi@example.com",
                  "phone": "+393331234567",
                  "city": "Milano",
                  "created_at": "2026-08-03T14:32:11Z"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMediaType"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      },
      "patch": {
        "tags": [
          "Customers"
        ],
        "summary": "Update a customer",
        "x-scope": "customers.write",
        "x-summary-it": "Aggiorna un cliente",
        "x-description-it": "Aggiorna solo i campi presenti nel corpo della richiesta: quelli omessi\nrestano invariati. Per svuotare un campo inviagli esplicitamente `null`.\n",
        "description": "Partial update following the design philosophy:\n\n- **Omitted JSON keys** leave the corresponding entity fields\n  untouched. The mobile UI can send only the keys it actually\n  changed.\n- **Explicit JSON `null`** clears the field (sets the column\n  to SQL NULL in the database). Use this to wipe an optional\n  field — there is no `?clear=phone` query param.\n- **Concrete values** overwrite the entity field with the value.\n\n`customer_type` is NOT NULL in the schema; sending it as\n`null` is a 422 `parameter_invalid` error.\n\nReturns the fully materialized post-update customer.\n\nForeign-key references in the body (`price_list_id`, etc.) must\nresolve to resources owned by the authenticated user's company.\nUnreachable refs return 422 `parameter_invalid` pointing at the\noffending field.\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CustomerPatchRequest"
              },
              "example": {
                "full_name": "Mario Rossi",
                "phone": null,
                "email": "mario.rossi@example.com"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Customer updated. Returns the full post-update entity.",
            "x-description-it": "Cliente aggiornato. Restituisce la scheda completa.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Customer"
                },
                "example": {
                  "object": "customer",
                  "id": "8f173fd2-6fa4-46e5-a8e3-23dfff31a0a1",
                  "customer_type": "private",
                  "full_name": "Mario Rossi",
                  "email": "mario.rossi@example.com",
                  "phone": null,
                  "city": "Milano",
                  "created_at": "2026-08-03T14:32:11Z"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/MalformedRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMediaType"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      },
      "delete": {
        "tags": [
          "Customers"
        ],
        "summary": "Soft-delete a customer",
        "x-scope": "customers.write",
        "x-summary-it": "Elimina un cliente",
        "x-description-it": "Eliminazione logica: il cliente sparisce dagli elenchi ma i documenti\ngià emessi restano validi e collegati.\n",
        "description": "Marks the customer as deleted by stamping `deleted_at` with the\nserver clock. The row stays in the database; subsequent\nGET/PATCH/DELETE all 404.\n\nReturns the deleted resource (200) rather than 204 No Content so\nthe response shape is consistent across the API (always a typed\nresource) and the mobile UI can render an undo banner with the\ntimestamp.\n\nRestoration is an admin-only operation; not exposed via this API.\n",
        "responses": {
          "200": {
            "description": "Customer soft-deleted. `deleted_at` is populated.",
            "x-description-it": "Cliente eliminato. Il campo «deleted_at» viene valorizzato.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Customer"
                },
                "example": {
                  "object": "customer",
                  "id": "8f173fd2-6fa4-46e5-a8e3-23dfff31a0a1",
                  "customer_type": "private",
                  "full_name": "Mario Rossi",
                  "email": "mario.rossi@example.com",
                  "phone": "+393331234567",
                  "city": "Milano",
                  "created_at": "2026-08-03T14:32:11Z"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/customers/{id}/documents": {
      "parameters": [
        {
          "$ref": "#/components/parameters/APIVersion"
        },
        {
          "name": "id",
          "in": "path",
          "required": true,
          "description": "Customer id (UUID).",
          "schema": {
            "type": "string"
          }
        }
      ],
      "post": {
        "tags": [
          "Customers"
        ],
        "summary": "Upload a document",
        "x-scope": "customers.write",
        "x-summary-it": "Carica un documento",
        "x-description-it": "Allega un file alla scheda del cliente.\nLa richiesta è `multipart/form-data`.\n",
        "description": "Attaches a file to the customer — the one multipart write on this\nAPI (`Content-Type: multipart/form-data`). Storage layout matches\nthe web upload, so documents from either surface are identical.\nMax 25MB.\n",
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "required": [
                  "file"
                ],
                "properties": {
                  "file": {
                    "type": "string",
                    "format": "binary"
                  },
                  "description": {
                    "type": "string",
                    "description": "Display label; defaults to the file name."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The stored document (url = its download route).",
            "x-description-it": "Il documento salvato («url» è la rotta di download).",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CustomerDocument"
                },
                "example": {
                  "object": "customer_document",
                  "id": "3b91ee0c-19b4-4f0a-9c2e-7d51a0c88e14",
                  "name": "libretto.pdf",
                  "content_type": "application/pdf",
                  "file_size_bytes": 184320,
                  "url": "/v1/customers/8f173fd2-6fa4-46e5-a8e3-23dfff31a0a1/documents/3b91ee0c-19b4-4f0a-9c2e-7d51a0c88e14/download",
                  "created_at": "2026-08-03T14:41:02Z"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMediaType"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/customers/{id}/documents/{documentID}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/APIVersion"
        },
        {
          "name": "id",
          "in": "path",
          "required": true,
          "description": "Identifier of the target resource.",
          "schema": {
            "type": "string"
          }
        },
        {
          "name": "documentID",
          "in": "path",
          "required": true,
          "description": "Identifier of the target resource.",
          "schema": {
            "type": "string"
          }
        }
      ],
      "delete": {
        "tags": [
          "Customers"
        ],
        "summary": "Delete customer document",
        "x-scope": "customers.write",
        "x-summary-it": "Elimina un documento",
        "x-description-it": "Rimuove il file allegato alla scheda del cliente.\n",
        "operationId": "customerDocumentDelete",
        "description": "CustomerDocumentDelete handles\nDELETE /v1/customers/{id}/documents/{documentID}.\n",
        "responses": {
          "200": {
            "description": "success (the removed document)",
            "x-description-it": "Documento eliminato.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CustomerDocument"
                },
                "example": {
                  "object": "customer_document",
                  "id": "3b91ee0c-19b4-4f0a-9c2e-7d51a0c88e14",
                  "name": "libretto.pdf",
                  "content_type": "application/pdf",
                  "file_size_bytes": 184320,
                  "url": "/v1/customers/8f173fd2-6fa4-46e5-a8e3-23dfff31a0a1/documents/3b91ee0c-19b4-4f0a-9c2e-7d51a0c88e14/download",
                  "created_at": "2026-08-03T14:41:02Z"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/customers/{id}/documents/{documentID}/download": {
      "parameters": [
        {
          "$ref": "#/components/parameters/APIVersion"
        },
        {
          "name": "id",
          "in": "path",
          "required": true,
          "description": "Customer id (UUID).",
          "schema": {
            "type": "string"
          }
        },
        {
          "name": "documentID",
          "in": "path",
          "required": true,
          "description": "Document id (UUID).",
          "schema": {
            "type": "string"
          }
        }
      ],
      "get": {
        "tags": [
          "Customers"
        ],
        "summary": "Download a document",
        "x-scope": "customers.read",
        "x-summary-it": "Scarica un documento",
        "x-description-it": "Restituisce un link temporaneo per scaricare il file allegato.\n",
        "description": "Streams the document bytes with the stored `Content-Type` and an\n`attachment` disposition — the target of every\n`customer_document.url`. Bearer-authenticated like every other\nroute; clients save the bytes and hand them to the OS viewer.\n",
        "responses": {
          "200": {
            "description": "The file bytes.",
            "x-description-it": "Il contenuto del file.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/octet-stream": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/dealership-report/{widget}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/APIVersion"
        },
        {
          "name": "widget",
          "in": "path",
          "required": true,
          "description": "Identifier of the target resource.",
          "schema": {
            "type": "string"
          }
        }
      ],
      "get": {
        "tags": [
          "Reports"
        ],
        "summary": "Dealership report widget",
        "x-summary-it": "Recupera un widget concessionaria",
        "x-scope": "reports.read",
        "x-description-it": "Come `GET /v1/report/{widget}`, ma per le statistiche della\nconcessionaria. Stessa busta di risposta.\n",
        "operationId": "dealershipReportWidget",
        "description": "the Situazione Concessionaria dashboard, mirroring the officina\nreport surface (one route per widget, progressive loading, the\nsame date filter as the list endpoints).\n\nWidget keys:\n\nkpis                    the web KPI row bundled: vehicles-sold,\ntotal-revenue, total-margin, average-days,\naverage-profit-per-vehicle,\nimmobilized-capital, loss-sales-percentage,\naverage-markup-percentage — each with the\nprevious-period delta\nsales-chart             revenue + margin series per period bucket\ntop-profitable-sales    ranked best sales by profit\nbottom-profitable-sales ranked worst sales by profit\ntop-giacenza-sales      fastest vehicles (lowest days in stock)\nbottom-giacenza-sales   slowest vehicles (highest days in stock)\n\nGate: dealership.read + reports.read, like the web dashboard (the\nmodule flag gates the menu client-side).\n",
        "parameters": [
          {
            "name": "from",
            "in": "query",
            "required": false,
            "description": "Inizio dell'intervallo, formato `YYYY-MM-DD`.",
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/DatePreset"
          },
          {
            "name": "to",
            "in": "query",
            "required": false,
            "description": "Fine dell'intervallo, formato `YYYY-MM-DD`.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "success",
            "x-description-it": "I dati del widget concessionaria.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/json": {
                "example": {
                  "object": "report_widget",
                  "widget": "stock_aging",
                  "preset": "year",
                  "from": "2026-01-01",
                  "to": "2026-12-31",
                  "data": [
                    {
                      "label": "0-30 giorni",
                      "value": 4
                    },
                    {
                      "label": "31-90 giorni",
                      "value": 7
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/delivery-notes": {
      "parameters": [
        {
          "$ref": "#/components/parameters/APIVersion"
        }
      ],
      "get": {
        "tags": [
          "Suppliers"
        ],
        "summary": "List delivery notes",
        "x-summary-it": "Elenca i documenti di trasporto",
        "x-scope": "suppliers.read",
        "x-description-it": "I DDT ricevuti dai fornitori, in sola lettura.\n",
        "operationId": "deliveryNoteList",
        "description": "trasporto\" (DDT) list.\n\n\n\nThe date filter defaults to the last 12 months; the applied\nrange is echoed in `date_filter`. Scoped to the authenticated user's\ncompany and gated on the articles.read permission (web parity — DDT lives\nunder the inventory module).\n",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": false,
            "description": "optional substring match on the supplier name (web parity)\n",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "description": "\"draft\" | \"issued\" — the tab filter; omitted returns\n",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "both",
            "in": "query",
            "required": false,
            "description": "(\"Tutti\" tab)\n",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "supplier_id",
            "in": "query",
            "required": false,
            "description": "narrow to a single supplier\n",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "sort,",
            "in": "query",
            "required": false,
            "description": "direction named sort key (date/number/supplier/total) +\nasc|desc;\n",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "default",
            "in": "query",
            "required": false,
            "description": "date desc\n",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "from,",
            "in": "query",
            "required": false,
            "description": "to YYYY-MM-DD date range bounds (on\ndelivery_notes.created_at)\n",
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/DatePreset"
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "page size (default 20, max 100)\n",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "starting_after",
            "in": "query",
            "required": false,
            "description": "opaque cursor from a previous response\n",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "direction",
            "in": "query",
            "required": false,
            "description": "Direzione di ordinamento: `asc` o `desc`.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "from",
            "in": "query",
            "required": false,
            "description": "Inizio dell'intervallo, formato `YYYY-MM-DD`.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "sort",
            "in": "query",
            "required": false,
            "description": "Colonna di ordinamento.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "to",
            "in": "query",
            "required": false,
            "description": "Fine dell'intervallo, formato `YYYY-MM-DD`.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "+ date_filter    success",
            "x-description-it": "I documenti di trasporto.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ListEnvelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/DeliveryNote"
                          }
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "object": "list",
                  "data": [
                    {
                      "object": "delivery_note",
                      "id": "9d1e6b48-5c20-4f37-a814-3b7e2d9f5a61",
                      "seq_number": 77,
                      "supplier_id": "6f2b8c14-9d70-4a35-b8e2-1c5f7a3d9e60",
                      "supplier_name": "Ricambi Lombardia S.r.l.",
                      "description": "Consegna filtri e olio",
                      "status": "issued",
                      "delivery_date": "2026-07-02",
                      "total_before_tax": 128.0,
                      "total_tax": 28.16,
                      "total_after_tax": 156.16,
                      "created_at": "2026-07-02T11:14:00Z"
                    }
                  ],
                  "has_more": false,
                  "url": "/v1/delivery-notes",
                  "date_filter": {
                    "object": "date_filter",
                    "preset": "year",
                    "from": "2026-01-01",
                    "to": "2026-12-31"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/delivery-notes/status-counts": {
      "parameters": [
        {
          "$ref": "#/components/parameters/APIVersion"
        }
      ],
      "get": {
        "tags": [
          "Suppliers"
        ],
        "summary": "Delivery note status counts",
        "x-summary-it": "Conta i DDT per stato",
        "x-scope": "suppliers.read",
        "x-description-it": "Il numero di documenti di trasporto in ciascuno stato, con gli stessi\nfiltri dell'elenco.\n",
        "operationId": "deliveryNoteStatusCounts",
        "description": "Bozza / Emessi tab counters. Counts respect the date range, the\nfree-text search and the selected supplier (web parity). The applied range\nis echoed in date_filter.\n",
        "parameters": [
          {
            "name": "from",
            "in": "query",
            "required": false,
            "description": "Inizio dell'intervallo, formato `YYYY-MM-DD`.",
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/DatePreset"
          },
          {
            "name": "q",
            "in": "query",
            "required": false,
            "description": "Ricerca testuale libera.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "supplier_id",
            "in": "query",
            "required": false,
            "description": "Filtra per fornitore.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "to",
            "in": "query",
            "required": false,
            "description": "Fine dell'intervallo, formato `YYYY-MM-DD`.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "success",
            "x-description-it": "Il numero di DDT per stato.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeliveryNoteStatusCounts"
                },
                "example": {
                  "object": "delivery_note_status_counts",
                  "draft": 1,
                  "issued": 64,
                  "date_filter": {
                    "object": "date_filter",
                    "preset": "year",
                    "from": "2026-01-01",
                    "to": "2026-12-31"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/electronic-invoices": {
      "parameters": [
        {
          "$ref": "#/components/parameters/APIVersion"
        }
      ],
      "get": {
        "tags": [
          "ElectronicInvoicing"
        ],
        "summary": "List electronic invoices",
        "x-summary-it": "Elenca le fatture elettroniche emesse",
        "x-scope": "invoices.read",
        "x-description-it": "Le fatture elettroniche trasmesse allo SdI, con il loro stato\n(`sent`, `delivered`, `rejected`).\n\nL'intero gruppo è in sola lettura: la trasmissione allo SdI non è\nesposta su questa API. Rientra nel permesso `invoices`, non ne ha uno\nproprio.\n",
        "operationId": "electronicInvoiceList",
        "description": "(attive) transmissions, newest first.\n\ndocument_type (TD01/TD04/scontrino, \"\"=all), from/to/preset\n(default last 12 months, echoed in date_filter).\n",
        "parameters": [
          {
            "name": "document_type",
            "in": "query",
            "required": false,
            "description": "Filtra per tipo documento (es. `TD01`).",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "from",
            "in": "query",
            "required": false,
            "description": "Inizio dell'intervallo, formato `YYYY-MM-DD`.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Numero di risultati per pagina.",
            "schema": {
              "type": "integer"
            }
          },
          {
            "$ref": "#/components/parameters/DatePreset"
          },
          {
            "name": "q",
            "in": "query",
            "required": false,
            "description": "Ricerca testuale libera.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "starting_after",
            "in": "query",
            "required": false,
            "description": "Cursore opaco della pagina precedente. Va restituito invariato.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "description": "Filtra per stato.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "to",
            "in": "query",
            "required": false,
            "description": "Fine dell'intervallo, formato `YYYY-MM-DD`.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "success",
            "x-description-it": "Le fatture elettroniche emesse.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/json": {
                "example": {
                  "object": "list",
                  "data": [
                    {
                      "object": "electronic_invoice",
                      "id": "6b2f9d54-8c17-4a03-be91-2d7c5a4f8e30",
                      "status": "delivered",
                      "document_type": "TD01",
                      "invoice_id": "4412e7b9-0c35-4a18-92d7-5b6ea1f38c04",
                      "credit_note_id": null,
                      "receipt_number": null,
                      "seq_number": 214,
                      "customer_name": "Mario Rossi",
                      "vehicle_name": "Fiat Panda",
                      "total_after_discount": 285.36,
                      "date": "2026-07-16",
                      "sent_at": "2026-07-16T10:35:00Z",
                      "delivered_at": "2026-07-16T11:02:00Z",
                      "rejected_at": null,
                      "created_at": "2026-07-16T10:35:00Z"
                    }
                  ],
                  "has_more": false,
                  "url": "/v1/electronic-invoices",
                  "date_filter": {
                    "object": "date_filter",
                    "preset": "year",
                    "from": "2026-01-01",
                    "to": "2026-12-31"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/electronic-invoices-incoming": {
      "parameters": [
        {
          "$ref": "#/components/parameters/APIVersion"
        }
      ],
      "get": {
        "tags": [
          "ElectronicInvoicing"
        ],
        "summary": "List electronic invoice incomings",
        "x-summary-it": "Elenca le fatture elettroniche ricevute",
        "x-scope": "invoices.read",
        "x-description-it": "Le fatture elettroniche ricevute dai fornitori. `expense_id` è\nvalorizzato quando la fattura è già stata registrata come spesa.\n",
        "operationId": "electronicInvoiceIncomingList",
        "description": "— the passive supplier invoices synced from ACube.\n\nFatturaPA code, \"\"=all), supplier_id, from/to/preset (default\nlast 12 months).\n",
        "parameters": [
          {
            "name": "document_type",
            "in": "query",
            "required": false,
            "description": "Filtra per tipo documento (es. `TD01`).",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "from",
            "in": "query",
            "required": false,
            "description": "Inizio dell'intervallo, formato `YYYY-MM-DD`.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Numero di risultati per pagina.",
            "schema": {
              "type": "integer"
            }
          },
          {
            "$ref": "#/components/parameters/DatePreset"
          },
          {
            "name": "q",
            "in": "query",
            "required": false,
            "description": "Ricerca testuale libera.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "starting_after",
            "in": "query",
            "required": false,
            "description": "Cursore opaco della pagina precedente. Va restituito invariato.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "description": "Filtra per stato.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "supplier_id",
            "in": "query",
            "required": false,
            "description": "Filtra per fornitore.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "to",
            "in": "query",
            "required": false,
            "description": "Fine dell'intervallo, formato `YYYY-MM-DD`.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "success",
            "x-description-it": "Le fatture elettroniche ricevute.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/json": {
                "example": {
                  "object": "list",
                  "data": [
                    {
                      "object": "electronic_invoice_incoming",
                      "id": "4c7e1b90-6d23-4a85-9f31-7b2c8e5d4a06",
                      "supplier_id": "6f2b8c14-9d70-4a35-b8e2-1c5f7a3d9e60",
                      "business_name": "Ricambi Lombardia S.r.l.",
                      "invoice_number": "2026/1184",
                      "invoice_date": "2026-07-28",
                      "total_amount": 512.4,
                      "status": "delivered",
                      "document_type": "TD01",
                      "expense_id": "ba38f519-4c72-4e06-9d81-5a2c7f4b3e90",
                      "created_at": "2026-07-29T06:00:00Z"
                    }
                  ],
                  "has_more": false,
                  "url": "/v1/electronic-invoices-incoming",
                  "date_filter": {
                    "object": "date_filter",
                    "preset": "year",
                    "from": "2026-01-01",
                    "to": "2026-12-31"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/electronic-invoices-incoming/status-counts": {
      "parameters": [
        {
          "$ref": "#/components/parameters/APIVersion"
        }
      ],
      "get": {
        "tags": [
          "ElectronicInvoicing"
        ],
        "summary": "Electronic invoice incoming status counts",
        "x-summary-it": "Conta le fatture ricevute per stato",
        "x-scope": "invoices.read",
        "x-description-it": "Il numero di fatture ricevute in ciascuno stato, con gli stessi\nfiltri dell'elenco.\n",
        "operationId": "electronicInvoiceIncomingStatusCounts",
        "description": "/v1/electronic-invoices-incoming/status-counts — the Passive tab counters\n(Ricevute / In quarantena), same filters as the list minus status.\n",
        "parameters": [
          {
            "name": "document_type",
            "in": "query",
            "required": false,
            "description": "Filtra per tipo documento (es. `TD01`).",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "from",
            "in": "query",
            "required": false,
            "description": "Inizio dell'intervallo, formato `YYYY-MM-DD`.",
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/DatePreset"
          },
          {
            "name": "q",
            "in": "query",
            "required": false,
            "description": "Ricerca testuale libera.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "supplier_id",
            "in": "query",
            "required": false,
            "description": "Filtra per fornitore.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "to",
            "in": "query",
            "required": false,
            "description": "Fine dell'intervallo, formato `YYYY-MM-DD`.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "success",
            "x-description-it": "Il numero di fatture ricevute per stato.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/json": {
                "example": {
                  "object": "electronic_invoice_incoming_status_counts",
                  "received": 214,
                  "quarantena": 0,
                  "date_filter": {
                    "object": "date_filter",
                    "preset": "year",
                    "from": "2026-01-01",
                    "to": "2026-12-31"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/electronic-invoices/status-counts": {
      "parameters": [
        {
          "$ref": "#/components/parameters/APIVersion"
        }
      ],
      "get": {
        "tags": [
          "ElectronicInvoicing"
        ],
        "summary": "Electronic invoice status counts",
        "x-summary-it": "Conta le fatture elettroniche per stato",
        "x-scope": "invoices.read",
        "x-description-it": "Il numero di fatture elettroniche in ciascuno stato, con gli stessi\nfiltri dell'elenco.\n",
        "operationId": "electronicInvoiceStatusCounts",
        "description": "/v1/electronic-invoices/status-counts — the Attive tab counters. Uses the\nsame filters as the list minus status (so the counts stay stable while\nswitching tabs, web parity).\n",
        "parameters": [
          {
            "name": "document_type",
            "in": "query",
            "required": false,
            "description": "Filtra per tipo documento (es. `TD01`).",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "from",
            "in": "query",
            "required": false,
            "description": "Inizio dell'intervallo, formato `YYYY-MM-DD`.",
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/DatePreset"
          },
          {
            "name": "q",
            "in": "query",
            "required": false,
            "description": "Ricerca testuale libera.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "to",
            "in": "query",
            "required": false,
            "description": "Fine dell'intervallo, formato `YYYY-MM-DD`.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "success",
            "x-description-it": "Il numero di fatture elettroniche per stato.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/json": {
                "example": {
                  "object": "electronic_invoice_status_counts",
                  "sent": 3,
                  "delivered": 128,
                  "rejected": 1,
                  "date_filter": {
                    "object": "date_filter",
                    "preset": "year",
                    "from": "2026-01-01",
                    "to": "2026-12-31"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/expense-categories": {
      "parameters": [
        {
          "$ref": "#/components/parameters/APIVersion"
        }
      ],
      "get": {
        "tags": [
          "Expenses"
        ],
        "summary": "List expense categories (unpaginated)",
        "x-summary-it": "Elenca le categorie di spesa",
        "x-scope": "expenses.read",
        "x-description-it": "Le categorie configurate dall'officina, da usare per valorizzare\n`expense_category_id`.\n",
        "responses": {
          "200": {
            "description": "Categories.",
            "x-description-it": "Le categorie di spesa.",
            "content": {
              "application/json": {
                "example": {
                  "object": "list",
                  "data": [
                    {
                      "object": "expense_category",
                      "id": "1a6f8d47-3c92-4e05-b731-8d2b5a9c4e60",
                      "name": "Ricambi"
                    },
                    {
                      "object": "expense_category",
                      "id": "5d2c9b78-4e13-40fa-8265-3b7a1f6d9c04",
                      "name": "Utenze"
                    }
                  ],
                  "has_more": false,
                  "url": "/v1/expense-categories"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/expenses": {
      "parameters": [
        {
          "$ref": "#/components/parameters/APIVersion"
        }
      ],
      "get": {
        "tags": [
          "Expenses"
        ],
        "summary": "List expenses",
        "x-summary-it": "Elenca le spese",
        "x-scope": "expenses.read",
        "x-description-it": "Le spese registrate. Filtra per stato con `status`, per fornitore con\n`supplier_id` e per periodo con `from` / `to`.\n",
        "parameters": [
          {
            "name": "from",
            "in": "query",
            "required": false,
            "description": "Inizio dell'intervallo, formato `YYYY-MM-DD`.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Numero di risultati per pagina.",
            "schema": {
              "type": "integer"
            }
          },
          {
            "$ref": "#/components/parameters/DatePreset"
          },
          {
            "name": "q",
            "in": "query",
            "required": false,
            "description": "Ricerca testuale libera.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "starting_after",
            "in": "query",
            "required": false,
            "description": "Cursore opaco della pagina precedente. Va restituito invariato.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "description": "Filtra per stato.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "supplier_id",
            "in": "query",
            "required": false,
            "description": "Filtra per fornitore.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "to",
            "in": "query",
            "required": false,
            "description": "Fine dell'intervallo, formato `YYYY-MM-DD`.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Expenses + date_filter.",
            "x-description-it": "Le spese registrate.",
            "content": {
              "application/json": {
                "example": {
                  "object": "list",
                  "data": [
                    {
                      "object": "expense",
                      "id": "ba38f519-4c72-4e06-9d81-5a2c7f4b3e90",
                      "seq_number": 91,
                      "supplier_id": "6f2b8c14-9d70-4a35-b8e2-1c5f7a3d9e60",
                      "supplier_name": "Ricambi Lombardia S.r.l.",
                      "expense_category_id": "1a6f8d47-3c92-4e05-b731-8d2b5a9c4e60",
                      "category_name": "Ricambi",
                      "description": "Ricambi vari luglio",
                      "invoice_number": "2026/1184",
                      "comments": null,
                      "status": "to_pay",
                      "expired": false,
                      "payment_method": "MP05",
                      "payment_due_at": "2026-08-31T00:00:00Z",
                      "total_before_tax": 420.0,
                      "total_tax": 92.4,
                      "total_after_tax": 512.4,
                      "total_discount": 0,
                      "total_payments": 0,
                      "outstanding_payments": 512.4,
                      "is_stornata": false,
                      "created_at": "2026-07-28T09:15:00Z",
                      "updated_at": "2026-07-28T09:15:00Z"
                    }
                  ],
                  "has_more": false,
                  "url": "/v1/expenses",
                  "date_filter": {
                    "object": "date_filter",
                    "preset": "year",
                    "from": "2026-01-01",
                    "to": "2026-12-31"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      },
      "post": {
        "tags": [
          "Expenses"
        ],
        "summary": "Create an expense (totals-first)",
        "x-summary-it": "Registra una spesa",
        "x-scope": "expenses.write",
        "x-description-it": "Registra una spesa verso un fornitore. Gli importi si passano al\nnetto (`total_before_tax`) e come imposta (`total_tax`): il totale\nlordo è calcolato dal server.\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "supplier_id",
                  "total_before_tax",
                  "total_tax"
                ],
                "properties": {
                  "supplier_id": {
                    "type": "string"
                  },
                  "description": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "invoice_number": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "expense_category_id": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "payment_method": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "payment_due_at": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "format": "date-time"
                  },
                  "total_before_tax": {
                    "type": "number"
                  },
                  "total_tax": {
                    "type": "number"
                  },
                  "comments": {
                    "type": [
                      "string",
                      "null"
                    ]
                  }
                }
              },
              "example": {
                "supplier_id": "6f2b8c14-9d70-4a35-b8e2-1c5f7a3d9e60",
                "description": "Ricambi vari luglio",
                "invoice_number": "2026/1184",
                "payment_method": "MP05",
                "payment_due_at": "2026-08-31T00:00:00Z",
                "total_before_tax": 420.0,
                "total_tax": 92.4
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Expense.",
            "x-description-it": "La spesa registrata.",
            "content": {
              "application/json": {
                "example": {
                  "object": "expense",
                  "id": "ba38f519-4c72-4e06-9d81-5a2c7f4b3e90",
                  "seq_number": 91,
                  "supplier_id": "6f2b8c14-9d70-4a35-b8e2-1c5f7a3d9e60",
                  "supplier_name": "Ricambi Lombardia S.r.l.",
                  "expense_category_id": "1a6f8d47-3c92-4e05-b731-8d2b5a9c4e60",
                  "category_name": "Ricambi",
                  "description": "Ricambi vari luglio",
                  "invoice_number": "2026/1184",
                  "comments": null,
                  "status": "to_pay",
                  "expired": false,
                  "payment_method": "MP05",
                  "payment_due_at": "2026-08-31T00:00:00Z",
                  "total_before_tax": 420.0,
                  "total_tax": 92.4,
                  "total_after_tax": 512.4,
                  "total_discount": 0,
                  "total_payments": 0,
                  "outstanding_payments": 512.4,
                  "is_stornata": false,
                  "created_at": "2026-07-28T09:15:00Z",
                  "updated_at": "2026-07-28T09:15:00Z"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/MalformedRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/expenses/status-counts": {
      "parameters": [
        {
          "$ref": "#/components/parameters/APIVersion"
        }
      ],
      "get": {
        "tags": [
          "Expenses"
        ],
        "summary": "Tab counters (to_pay / expired / paid)",
        "x-summary-it": "Conta le spese per stato",
        "x-scope": "expenses.read",
        "x-description-it": "Il numero di spese in ciascuno stato, con gli stessi filtri\ndell'elenco.\n",
        "parameters": [
          {
            "name": "from",
            "in": "query",
            "required": false,
            "description": "Inizio dell'intervallo, formato `YYYY-MM-DD`.",
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/DatePreset"
          },
          {
            "name": "q",
            "in": "query",
            "required": false,
            "description": "Ricerca testuale libera.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "supplier_id",
            "in": "query",
            "required": false,
            "description": "Filtra per fornitore.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "to",
            "in": "query",
            "required": false,
            "description": "Fine dell'intervallo, formato `YYYY-MM-DD`.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Counts + date_filter.",
            "x-description-it": "Il numero di spese per stato.",
            "content": {
              "application/json": {
                "example": {
                  "object": "expense_status_counts",
                  "to_pay": 6,
                  "expired": 2,
                  "paid": 143,
                  "date_filter": {
                    "object": "date_filter",
                    "preset": "year",
                    "from": "2026-01-01",
                    "to": "2026-12-31"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/expenses/{id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/APIVersion"
        },
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        }
      ],
      "get": {
        "tags": [
          "Expenses"
        ],
        "summary": "Retrieve an expense (line items + installments)",
        "x-summary-it": "Recupera una spesa",
        "x-scope": "expenses.read",
        "x-description-it": "Restituisce la spesa con le righe, le rate, i pagamenti e il residuo\nda pagare (`outstanding_payments`).\n",
        "responses": {
          "200": {
            "description": "Expense.",
            "x-description-it": "La spesa richiesta.",
            "content": {
              "application/json": {
                "example": {
                  "object": "expense",
                  "id": "ba38f519-4c72-4e06-9d81-5a2c7f4b3e90",
                  "seq_number": 91,
                  "supplier_id": "6f2b8c14-9d70-4a35-b8e2-1c5f7a3d9e60",
                  "supplier_name": "Ricambi Lombardia S.r.l.",
                  "expense_category_id": "1a6f8d47-3c92-4e05-b731-8d2b5a9c4e60",
                  "category_name": "Ricambi",
                  "description": "Ricambi vari luglio",
                  "invoice_number": "2026/1184",
                  "comments": null,
                  "status": "to_pay",
                  "expired": false,
                  "payment_method": "MP05",
                  "payment_due_at": "2026-08-31T00:00:00Z",
                  "total_before_tax": 420.0,
                  "total_tax": 92.4,
                  "total_after_tax": 512.4,
                  "total_discount": 0,
                  "total_payments": 0,
                  "outstanding_payments": 512.4,
                  "is_stornata": false,
                  "created_at": "2026-07-28T09:15:00Z",
                  "updated_at": "2026-07-28T09:15:00Z"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      },
      "patch": {
        "tags": [
          "Expenses"
        ],
        "summary": "Update header fields and totals",
        "x-summary-it": "Aggiorna una spesa",
        "x-scope": "expenses.write",
        "x-description-it": "Aggiorna solo i campi presenti nel corpo: gli altri restano\ninvariati.\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "description": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "invoice_number": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "expense_category_id": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "payment_method": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "payment_due_at": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "format": "date-time"
                  },
                  "total_before_tax": {
                    "type": "number"
                  },
                  "total_tax": {
                    "type": "number"
                  },
                  "comments": {
                    "type": [
                      "string",
                      "null"
                    ]
                  }
                }
              },
              "example": {
                "description": "Ricambi vari luglio (rettificato)",
                "total_before_tax": 400.0,
                "total_tax": 88.0
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated expense.",
            "x-description-it": "La spesa aggiornata.",
            "content": {
              "application/json": {
                "example": {
                  "object": "expense",
                  "id": "ba38f519-4c72-4e06-9d81-5a2c7f4b3e90",
                  "seq_number": 91,
                  "supplier_id": "6f2b8c14-9d70-4a35-b8e2-1c5f7a3d9e60",
                  "supplier_name": "Ricambi Lombardia S.r.l.",
                  "expense_category_id": "1a6f8d47-3c92-4e05-b731-8d2b5a9c4e60",
                  "category_name": "Ricambi",
                  "description": "Ricambi vari luglio",
                  "invoice_number": "2026/1184",
                  "comments": null,
                  "status": "to_pay",
                  "expired": false,
                  "payment_method": "MP05",
                  "payment_due_at": "2026-08-31T00:00:00Z",
                  "total_before_tax": 420.0,
                  "total_tax": 92.4,
                  "total_after_tax": 512.4,
                  "total_discount": 0,
                  "total_payments": 0,
                  "outstanding_payments": 512.4,
                  "is_stornata": false,
                  "created_at": "2026-07-28T09:15:00Z",
                  "updated_at": "2026-07-28T09:15:00Z"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/MalformedRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      },
      "delete": {
        "tags": [
          "Expenses"
        ],
        "summary": "Delete an expense",
        "x-summary-it": "Elimina una spesa",
        "x-scope": "expenses.write",
        "x-description-it": "Eliminazione logica della spesa.\n",
        "responses": {
          "200": {
            "description": "The removed expense.",
            "x-description-it": "La spesa eliminata.",
            "content": {
              "application/json": {
                "example": {
                  "object": "expense",
                  "id": "ba38f519-4c72-4e06-9d81-5a2c7f4b3e90",
                  "seq_number": 91,
                  "supplier_id": "6f2b8c14-9d70-4a35-b8e2-1c5f7a3d9e60",
                  "supplier_name": "Ricambi Lombardia S.r.l.",
                  "expense_category_id": "1a6f8d47-3c92-4e05-b731-8d2b5a9c4e60",
                  "category_name": "Ricambi",
                  "description": "Ricambi vari luglio",
                  "invoice_number": "2026/1184",
                  "comments": null,
                  "status": "to_pay",
                  "expired": false,
                  "payment_method": "MP05",
                  "payment_due_at": "2026-08-31T00:00:00Z",
                  "total_before_tax": 420.0,
                  "total_tax": 92.4,
                  "total_after_tax": 512.4,
                  "total_discount": 0,
                  "total_payments": 0,
                  "outstanding_payments": 512.4,
                  "is_stornata": false,
                  "created_at": "2026-07-28T09:15:00Z",
                  "updated_at": "2026-07-28T09:15:00Z"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/expenses/{id}/payments": {
      "parameters": [
        {
          "$ref": "#/components/parameters/APIVersion"
        },
        {
          "name": "id",
          "in": "path",
          "required": true,
          "description": "Identifier of the target resource.",
          "schema": {
            "type": "string"
          }
        }
      ],
      "get": {
        "tags": [
          "Expenses"
        ],
        "summary": "List expense payments",
        "x-summary-it": "Elenca i pagamenti della spesa",
        "x-scope": "expenses.read",
        "x-description-it": "I pagamenti registrati sulla spesa. Il residuo è\n`outstanding_payments` sulla spesa stessa.\n",
        "operationId": "expensePaymentList",
        "responses": {
          "200": {
            "description": "success",
            "x-description-it": "I pagamenti della spesa.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/json": {
                "example": {
                  "object": "list",
                  "data": [
                    {
                      "object": "expense_payment",
                      "id": "d47c0b58-3e91-42a6-8f05-1b9d6a3e2c74",
                      "amount": 512.4,
                      "payment_method": "MP05",
                      "payment_date": "2026-08-01",
                      "financial_account_id": "d61c8b04-3f97-4a25-8e70-9b2d5f1a6c38",
                      "created_at": "2026-08-01T10:00:00Z"
                    }
                  ],
                  "has_more": false,
                  "url": "/v1/expenses/ba38f519-4c72-4e06-9d81-5a2c7f4b3e90/payments"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      },
      "post": {
        "tags": [
          "Expenses"
        ],
        "summary": "Create an expense payment",
        "x-summary-it": "Registra un pagamento",
        "x-scope": "expenses.write",
        "x-description-it": "Registra un pagamento sulla spesa. `payment_date` è nel formato\n`YYYY-MM-DD` e vale la data odierna se omesso. L'importo può essere\nparziale.\n\nA differenza di ogni altro endpoint, la risposta è un `201` **senza\ncorpo**: il pagamento non viene restituito. Rileggi la spesa per\nvedere i totali aggiornati.\n",
        "operationId": "expensePaymentCreate",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "amount"
                ],
                "properties": {
                  "amount": {
                    "type": "number"
                  },
                  "payment_method": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "payment_date": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "description": "Formato `YYYY-MM-DD`, fra 1900-01-01 e 2100-12-31. Se omesso\nvale la data odierna.\n"
                  },
                  "financial_account_id": {
                    "type": [
                      "string",
                      "null"
                    ]
                  }
                }
              },
              "example": {
                "amount": 512.4,
                "payment_method": "MP05",
                "payment_date": "2026-08-01"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created. Unlike every other endpoint on this API this one\nanswers with an EMPTY body — the payment is not echoed back.\nRe-read the expense to see the updated totals.\n",
            "x-description-it": "Pagamento registrato. Risposta senza corpo."
          },
          "400": {
            "$ref": "#/components/responses/MalformedRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/expenses/{id}/payments/{paymentID}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/APIVersion"
        },
        {
          "name": "id",
          "in": "path",
          "required": true,
          "description": "Identifier of the target resource.",
          "schema": {
            "type": "string"
          }
        },
        {
          "name": "paymentID",
          "in": "path",
          "required": true,
          "description": "Identifier of the target resource.",
          "schema": {
            "type": "string"
          }
        }
      ],
      "delete": {
        "tags": [
          "Expenses"
        ],
        "summary": "Delete expense payment",
        "x-summary-it": "Elimina un pagamento",
        "x-scope": "expenses.write",
        "x-description-it": "Rimuove il pagamento e riporta il residuo della spesa al valore\nprecedente.\n",
        "operationId": "expensePaymentDelete",
        "responses": {
          "204": {
            "description": "Deleted. No body — unlike the invoice and worksheet payment\ndeletes, which echo the removed payment.\n",
            "x-description-it": "Pagamento eliminato. Risposta senza corpo."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/financial-accounts": {
      "parameters": [
        {
          "$ref": "#/components/parameters/APIVersion"
        }
      ],
      "get": {
        "tags": [
          "CashBook"
        ],
        "summary": "List the company's financial accounts",
        "x-summary-it": "Elenca i conti",
        "x-scope": "cashbook.read",
        "x-description-it": "I conti configurati (cassa, banca), da usare per valorizzare\n`financial_account_id` sui pagamenti.\n",
        "description": "The conti (Banca, Cassa, …) — the options of the prima nota\nConto filter, mirroring the web cashbook's account dropdown.\nUnpaginated; read-only (accounts are managed in the web\nsettings).\n",
        "responses": {
          "200": {
            "description": "The company's accounts.",
            "x-description-it": "I conti configurati.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "object",
                    "data",
                    "has_more",
                    "url"
                  ],
                  "properties": {
                    "object": {
                      "type": "string",
                      "const": "list"
                    },
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "required": [
                          "object",
                          "id",
                          "type",
                          "name"
                        ],
                        "properties": {
                          "object": {
                            "type": "string",
                            "const": "financial_account"
                          },
                          "id": {
                            "type": "string"
                          },
                          "type": {
                            "type": "string",
                            "example": "bank"
                          },
                          "name": {
                            "type": "string",
                            "example": "Banca"
                          }
                        }
                      }
                    },
                    "has_more": {
                      "type": "boolean",
                      "const": false
                    },
                    "url": {
                      "type": "string"
                    }
                  }
                },
                "example": {
                  "object": "list",
                  "data": [
                    {
                      "object": "financial_account",
                      "id": "d61c8b04-3f97-4a25-8e70-9b2d5f1a6c38",
                      "type": "cassa",
                      "name": "Cassa"
                    },
                    {
                      "object": "financial_account",
                      "id": "7e3c9a25-1b68-4f40-9d72-5a8e2c6b3f19",
                      "type": "banca",
                      "name": "Banca Intesa"
                    }
                  ],
                  "has_more": false,
                  "url": "/v1/financial-accounts"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      },
      "post": {
        "tags": [
          "CashBook"
        ],
        "summary": "Create a financial account",
        "x-summary-it": "Crea un conto",
        "x-scope": "cashbook.write",
        "x-description-it": "Crea un conto finanziario. `type` distingue cassa e banca.\n",
        "operationId": "financialAccountCreate",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "name",
                  "type"
                ],
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "type": {
                    "type": "string",
                    "description": "Tipo di conto, es. `cassa` o `banca`."
                  }
                }
              },
              "example": {
                "name": "Banca Intesa",
                "type": "banca"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "success",
            "x-description-it": "Il conto creato.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/json": {
                "example": {
                  "object": "financial_account",
                  "name": "Banca Intesa",
                  "type": "banca"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/MalformedRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/financial-accounts/{id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/APIVersion"
        },
        {
          "name": "id",
          "in": "path",
          "required": true,
          "description": "Identifier of the target resource.",
          "schema": {
            "type": "string"
          }
        }
      ],
      "delete": {
        "tags": [
          "CashBook"
        ],
        "summary": "Delete financial account",
        "x-summary-it": "Elimina un conto",
        "x-scope": "cashbook.write",
        "x-description-it": "Elimina il conto. I movimenti già registrati su di esso restano\nintatti.\n",
        "operationId": "financialAccountDelete",
        "responses": {
          "200": {
            "description": "success",
            "x-description-it": "Il conto eliminato.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/json": {
                "example": {
                  "object": "financial_account",
                  "id": "7e3c9a25-1b68-4f40-9d72-5a8e2c6b3f19"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      },
      "patch": {
        "tags": [
          "CashBook"
        ],
        "summary": "Update financial account",
        "x-summary-it": "Aggiorna un conto",
        "x-scope": "cashbook.write",
        "x-description-it": "Aggiorna il nome del conto.\n",
        "operationId": "financialAccountPatch",
        "description": "(rename only — the type is fixed at creation, web parity).\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "name"
                ],
                "properties": {
                  "name": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "name": "Nuovo nome"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "success",
            "x-description-it": "Il conto aggiornato.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/json": {
                "example": {
                  "object": "financial_account",
                  "id": "7e3c9a25-1b68-4f40-9d72-5a8e2c6b3f19"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/invoices": {
      "parameters": [
        {
          "$ref": "#/components/parameters/APIVersion"
        }
      ],
      "get": {
        "tags": [
          "Invoices"
        ],
        "summary": "List invoices",
        "x-summary-it": "Elenca le fatture",
        "x-scope": "invoices.read",
        "x-description-it": "Le fatture dell'officina, dalla più recente. Filtra per stato con\n`status` (`draft` o `issued`), per cliente con `customer_id` e per\nperiodo con `from` / `to`. La paginazione usa un cursore opaco: passa\n`next_starting_after` invariato in `starting_after`.\n",
        "description": "Returns invoices in the authenticated user's company, newest\nfirst. Filter by name (`?q=`), payment state\n(`?payment_filter=pending|settled`), fiscal status\n(`?fiscal_status=draft|issued`), or date range\n(`?from=`/`?to=`/`?preset=`). Paginate with cursor-based\n`?limit=` + `?starting_after=`.\n\nThe date window defaults to the last 12 months; set it with\n`from`/`to`/`preset`. The applied range is echoed in\n`date_filter`.\n\nThe cursor is opaque — clients MUST NOT parse it. Pass back\nthe value verbatim. `has_more` drives the \"load more\" UI;\na full page may still be the last page.\n\nList rows OMIT `line_items` to keep payloads small. Fetch\nthe detail endpoint for the full body.\n",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": false,
            "description": "Substring match on invoice display fields.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "payment_filter",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "pending",
                "settled"
              ]
            }
          },
          {
            "name": "fiscal_status",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "draft",
                "issued"
              ]
            }
          },
          {
            "name": "sort",
            "in": "query",
            "required": false,
            "description": "Sort key (default `date` = emission date, i.e. `issued_at`\nfalling back to `created_at`). Named keys are mapped\nserver-side to whitelisted columns.\n",
            "schema": {
              "type": "string",
              "enum": [
                "date",
                "number",
                "customer",
                "vehicle",
                "total",
                "outstanding"
              ]
            }
          },
          {
            "name": "direction",
            "in": "query",
            "required": false,
            "description": "Sort direction (default `desc`).",
            "schema": {
              "type": "string",
              "enum": [
                "asc",
                "desc"
              ]
            }
          },
          {
            "name": "from",
            "in": "query",
            "required": false,
            "description": "Inclusive lower bound (YYYY-MM-DD).",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "to",
            "in": "query",
            "required": false,
            "description": "Inclusive upper bound (YYYY-MM-DD).",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "$ref": "#/components/parameters/DatePreset"
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 20
            }
          },
          {
            "name": "starting_after",
            "in": "query",
            "required": false,
            "description": "Opaque cursor from a prior response.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "without_electronic_invoice",
            "in": "query",
            "required": false,
            "description": "Solo le fatture senza controparte elettronica.",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Page of invoices (line_items omitted).",
            "x-description-it": "Una pagina di fatture.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ListEnvelope"
                    },
                    {
                      "type": "object",
                      "required": [
                        "date_filter"
                      ],
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/Invoice"
                          }
                        },
                        "date_filter": {
                          "$ref": "#/components/schemas/DateFilter"
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "object": "list",
                  "data": [
                    {
                      "object": "invoice",
                      "id": "4412e7b9-0c35-4a18-92d7-5b6ea1f38c04",
                      "status": "issued",
                      "seq_number": 214,
                      "title": "Tagliando 60.000 km",
                      "customer_id": "4c911f08-7713-4e83-bdf6-a0692a1b43fd",
                      "customer_name": "Mario Rossi",
                      "vehicle_id": "8821f0ac-5d2b-41e7-9a10-6c3f2b7d4e55",
                      "vehicle_name": "Fiat Panda",
                      "vehicle_license": "AB123CD",
                      "worksheet_id": "a5d21e64-8f37-4b90-ae12-3c7d6b048f95",
                      "sezionale_id": "c81f4a26-7d09-4e35-b6a1-2f8c5d3e9047",
                      "sezionale_code": "A",
                      "payment_method": "bonifico",
                      "payment_due_at": "2026-08-15T00:00:00Z",
                      "total_before_tax": 233.9,
                      "total_tax": 51.46,
                      "total_after_tax": 285.36,
                      "total_payments": 150.0,
                      "outstanding_payments": 135.36,
                      "sdi_status": "delivered",
                      "issued_at": "2026-07-16T10:30:00Z",
                      "created_at": "2026-07-16T10:12:00Z",
                      "updated_at": "2026-07-16T10:30:00Z"
                    }
                  ],
                  "has_more": true,
                  "next_starting_after": "eyJvIjoyMH0",
                  "url": "/v1/invoices",
                  "date_filter": {
                    "object": "date_filter",
                    "preset": "year",
                    "from": "2026-01-01",
                    "to": "2026-12-31"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMediaType"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      },
      "post": {
        "tags": [
          "Invoices"
        ],
        "summary": "Create a draft invoice from a worksheet",
        "x-summary-it": "Crea una fattura",
        "x-scope": "invoices.write",
        "x-description-it": "La fattura nasce sempre da una scheda lavoro: passa `worksheet_id` e\nrighe, totali e anagrafiche vengono copiati da lì. Non esiste un\nflusso «fattura vuota + aggiungi righe» su questa API.\n\nLa fattura creata è in stato `draft` e non ha ancora numero: il\nnumero viene assegnato da `POST /v1/invoices/{id}/issue`.\n",
        "description": "Snapshots the referenced worksheet's totals and line items\ninto a new draft invoice. The worksheet MUST belong to the\nauthenticated user's company; cross-tenant requests return\n404 (same as direct invoice access).\n\n`payment_due_at` is optional. When omitted the server\ncomputes today + the company's default payment term (e.g.\n`30_days`), matching the web flow.\n\nThe response includes the materialized `line_items` array.\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/InvoiceCreateRequest"
              },
              "example": {
                "worksheet_id": "a5d21e64-8f37-4b90-ae12-3c7d6b048f95",
                "payment_method": "bank_transfer",
                "payment_due_at": "2026-06-30T00:00:00Z"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Invoice created.",
            "x-description-it": "La fattura creata, in bozza.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Invoice"
                },
                "example": {
                  "object": "invoice",
                  "id": "4412e7b9-0c35-4a18-92d7-5b6ea1f38c04",
                  "status": "draft",
                  "seq_number": 214,
                  "title": "Tagliando 60.000 km",
                  "customer_id": "4c911f08-7713-4e83-bdf6-a0692a1b43fd",
                  "customer_name": "Mario Rossi",
                  "vehicle_id": "8821f0ac-5d2b-41e7-9a10-6c3f2b7d4e55",
                  "vehicle_name": "Fiat Panda",
                  "vehicle_license": "AB123CD",
                  "worksheet_id": "a5d21e64-8f37-4b90-ae12-3c7d6b048f95",
                  "sezionale_id": "c81f4a26-7d09-4e35-b6a1-2f8c5d3e9047",
                  "sezionale_code": "A",
                  "payment_method": "bonifico",
                  "payment_due_at": "2026-08-15T00:00:00Z",
                  "total_before_tax": 233.9,
                  "total_tax": 51.46,
                  "total_after_tax": 285.36,
                  "total_payments": 150.0,
                  "outstanding_payments": 135.36,
                  "sdi_status": "delivered",
                  "issued_at": "2026-07-16T10:30:00Z",
                  "created_at": "2026-07-16T10:12:00Z",
                  "updated_at": "2026-07-16T10:30:00Z"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/MalformedRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMediaType"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/invoices/payment-counts": {
      "parameters": [
        {
          "$ref": "#/components/parameters/APIVersion"
        }
      ],
      "get": {
        "tags": [
          "Invoices"
        ],
        "summary": "Count invoices per payment state",
        "x-summary-it": "Conta le fatture per stato di incasso",
        "x-scope": "invoices.read",
        "x-description-it": "Quante fatture risultano da incassare (`pending`) e quante saldate\n(`settled`), con gli stessi filtri dell'elenco.\n",
        "description": "Returns how many invoices are pending payment vs fully settled,\nfor the tab counters above the mobile list. Accepts the same\nfilters as `GET /v1/invoices` minus `payment_filter` and\npagination (`q`, `from`/`to`/`preset` with the same date-filter\nsemantics), so the numbers always match what the\nlist shows. The applied range is echoed in `date_filter`.\n",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": false,
            "description": "Substring match on invoice display fields.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "from",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "to",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "$ref": "#/components/parameters/DatePreset"
          },
          {
            "name": "fiscal_status",
            "in": "query",
            "required": false,
            "description": "Filtra per stato fiscale del documento.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Counts per payment state.",
            "x-description-it": "Il numero di fatture per stato di incasso.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InvoicePaymentCounts"
                },
                "example": {
                  "object": "invoice_payment_counts",
                  "pending": 7,
                  "settled": 132,
                  "date_filter": {
                    "object": "date_filter",
                    "preset": "year",
                    "from": "2026-01-01",
                    "to": "2026-12-31"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/invoices/{id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/APIVersion"
        },
        {
          "name": "id",
          "in": "path",
          "required": true,
          "description": "Invoice id (UUID).",
          "schema": {
            "type": "string"
          }
        }
      ],
      "get": {
        "tags": [
          "Invoices"
        ],
        "summary": "Retrieve an invoice",
        "x-summary-it": "Recupera una fattura",
        "x-scope": "invoices.read",
        "x-description-it": "Restituisce la fattura con le righe (`line_items`) e i totali. Il\ncampo `sdi_status` riporta lo stato della trasmissione elettronica in\nsola lettura: l'invio allo SdI non è esposto su questa API.\n",
        "description": "Returns a single invoice by id, scoped to the authenticated\nuser's company. Unknown ids, ids from another company, and\nsoft-deleted rows all return 404 — never 403, never 200 with\n`deleted_at` populated.\n\nIncludes the nested `line_items` array.\n",
        "responses": {
          "200": {
            "description": "Invoice found.",
            "x-description-it": "La fattura richiesta.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Invoice"
                },
                "example": {
                  "object": "invoice",
                  "id": "4412e7b9-0c35-4a18-92d7-5b6ea1f38c04",
                  "status": "issued",
                  "seq_number": 214,
                  "title": "Tagliando 60.000 km",
                  "customer_id": "4c911f08-7713-4e83-bdf6-a0692a1b43fd",
                  "customer_name": "Mario Rossi",
                  "vehicle_id": "8821f0ac-5d2b-41e7-9a10-6c3f2b7d4e55",
                  "vehicle_name": "Fiat Panda",
                  "vehicle_license": "AB123CD",
                  "worksheet_id": "a5d21e64-8f37-4b90-ae12-3c7d6b048f95",
                  "sezionale_id": "c81f4a26-7d09-4e35-b6a1-2f8c5d3e9047",
                  "sezionale_code": "A",
                  "payment_method": "bonifico",
                  "payment_due_at": "2026-08-15T00:00:00Z",
                  "total_before_tax": 233.9,
                  "total_tax": 51.46,
                  "total_after_tax": 285.36,
                  "total_payments": 150.0,
                  "outstanding_payments": 135.36,
                  "sdi_status": "delivered",
                  "issued_at": "2026-07-16T10:30:00Z",
                  "created_at": "2026-07-16T10:12:00Z",
                  "updated_at": "2026-07-16T10:30:00Z",
                  "line_items": [
                    {
                      "object": "invoice_line_item",
                      "id": "7c0f6c4e-9b1a-4c2e-9a5d-2f3b1e8a4d10",
                      "description": "Olio motore 5W30",
                      "quantity": 4,
                      "unit_type": "pz",
                      "unit_price": 18.5,
                      "vat_code": "22",
                      "tax_rate": 22,
                      "total_before_tax": 74.0,
                      "total_tax": 16.28,
                      "total_after_tax": 90.28
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMediaType"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      },
      "patch": {
        "tags": [
          "Invoices"
        ],
        "summary": "Update an invoice",
        "x-summary-it": "Aggiorna una fattura",
        "x-scope": "invoices.write",
        "x-description-it": "Aggiorna solo i campi presenti nel corpo: gli altri restano\ninvariati, e `null` svuota il campo. Una fattura già emessa accetta\nsolo modifiche che non incidono sui valori fiscali.\n",
        "description": "Partial update following the design philosophy:\n\n- Omitted JSON keys leave the corresponding entity fields\n  untouched.\n- Explicit JSON `null` clears the field (SQL NULL) where the\n  column is nullable.\n- Concrete values overwrite the entity field.\n\nNon-nullable columns (`payment_method`, `payment_condition`,\n`payment_due_at`, `vat_exigibility`) reject explicit `null`\nwith 422 `parameter_invalid`.\n\nFields NOT patchable through this endpoint: `id`,\n`seq_number`, `status`, `customer_id`, `vehicle_id`,\n`worksheet_id`, all totals, all timestamps except\n`payment_due_at`. Line items are not mutable via PATCH.\nUse POST `/v1/invoices/{id}/issue` to transition a draft.\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/InvoicePatchRequest"
              },
              "example": {
                "title": "Tagliando completo",
                "comments": null,
                "payment_method": "card",
                "payment_due_at": "2026-07-15T00:00:00Z"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Invoice updated.",
            "x-description-it": "La fattura aggiornata.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Invoice"
                },
                "example": {
                  "object": "invoice",
                  "id": "4412e7b9-0c35-4a18-92d7-5b6ea1f38c04",
                  "status": "issued",
                  "seq_number": 214,
                  "title": "Tagliando 60.000 km",
                  "customer_id": "4c911f08-7713-4e83-bdf6-a0692a1b43fd",
                  "customer_name": "Mario Rossi",
                  "vehicle_id": "8821f0ac-5d2b-41e7-9a10-6c3f2b7d4e55",
                  "vehicle_name": "Fiat Panda",
                  "vehicle_license": "AB123CD",
                  "worksheet_id": "a5d21e64-8f37-4b90-ae12-3c7d6b048f95",
                  "sezionale_id": "c81f4a26-7d09-4e35-b6a1-2f8c5d3e9047",
                  "sezionale_code": "A",
                  "payment_method": "bonifico",
                  "payment_due_at": "2026-08-15T00:00:00Z",
                  "total_before_tax": 233.9,
                  "total_tax": 51.46,
                  "total_after_tax": 285.36,
                  "total_payments": 150.0,
                  "outstanding_payments": 135.36,
                  "sdi_status": "delivered",
                  "issued_at": "2026-07-16T10:30:00Z",
                  "created_at": "2026-07-16T10:12:00Z",
                  "updated_at": "2026-07-16T10:30:00Z",
                  "line_items": [
                    {
                      "object": "invoice_line_item",
                      "id": "7c0f6c4e-9b1a-4c2e-9a5d-2f3b1e8a4d10",
                      "description": "Olio motore 5W30",
                      "quantity": 4,
                      "unit_type": "pz",
                      "unit_price": 18.5,
                      "vat_code": "22",
                      "tax_rate": 22,
                      "total_before_tax": 74.0,
                      "total_tax": 16.28,
                      "total_after_tax": 90.28
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/MalformedRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMediaType"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      },
      "delete": {
        "tags": [
          "Invoices"
        ],
        "summary": "Soft-delete an invoice",
        "x-summary-it": "Elimina una fattura",
        "x-scope": "invoices.write",
        "x-description-it": "Eliminazione logica della fattura.\n\nUna fattura la cui controparte elettronica è già stata trasmessa\nallo SdI non è eliminabile: la richiesta risponde `409`. In quel\ncaso il documento va stornato con una nota di credito\n(`POST /v1/credit-notes`), non eliminato.\n",
        "description": "Stamps `deleted_at`; the row stays in the database, and\nsubsequent GET/PATCH/DELETE/issue all 404. Returns the\ndeleted resource (200) for consistency with the rest of the\nAPI and so the client can render an undo banner.\n",
        "responses": {
          "200": {
            "description": "Invoice soft-deleted.",
            "x-description-it": "La fattura eliminata.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Invoice"
                },
                "example": {
                  "object": "invoice",
                  "id": "4412e7b9-0c35-4a18-92d7-5b6ea1f38c04",
                  "status": "issued",
                  "seq_number": 214,
                  "title": "Tagliando 60.000 km",
                  "customer_id": "4c911f08-7713-4e83-bdf6-a0692a1b43fd",
                  "customer_name": "Mario Rossi",
                  "vehicle_id": "8821f0ac-5d2b-41e7-9a10-6c3f2b7d4e55",
                  "vehicle_name": "Fiat Panda",
                  "vehicle_license": "AB123CD",
                  "worksheet_id": "a5d21e64-8f37-4b90-ae12-3c7d6b048f95",
                  "sezionale_id": "c81f4a26-7d09-4e35-b6a1-2f8c5d3e9047",
                  "sezionale_code": "A",
                  "payment_method": "bonifico",
                  "payment_due_at": "2026-08-15T00:00:00Z",
                  "total_before_tax": 233.9,
                  "total_tax": 51.46,
                  "total_after_tax": 285.36,
                  "total_payments": 150.0,
                  "outstanding_payments": 135.36,
                  "sdi_status": "delivered",
                  "issued_at": "2026-07-16T10:30:00Z",
                  "created_at": "2026-07-16T10:12:00Z",
                  "updated_at": "2026-07-16T10:30:00Z",
                  "deleted_at": "2026-07-20T11:04:00Z"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/invoices/{id}/convert-to-scontrino": {
      "parameters": [
        {
          "$ref": "#/components/parameters/APIVersion"
        },
        {
          "name": "id",
          "in": "path",
          "required": true,
          "description": "Invoice id (UUID).",
          "schema": {
            "type": "string"
          }
        }
      ],
      "post": {
        "tags": [
          "Invoices"
        ],
        "summary": "Convert a draft fattura into a scontrino",
        "x-summary-it": "Converti in scontrino",
        "x-scope": "invoices.write",
        "x-description-it": "Trasforma la fattura in uno scontrino, mantenendo righe e totali.\n\nAttenzione: la risposta è lo **scontrino** creato, non la fattura —\nl'oggetto restituito ha `object: receipt`.\n",
        "description": "Swaps a non-emessa, worksheet-backed draft invoice for a draft\nscontrino on the same scheda lavoro — the same conversion the web\nperforms (POST /hx/private/invoices/{id}/convert-to-scontrino).\n\nPayments move onto the new receipt; the source invoice is\nsoft-deleted; `worksheets.fiscal_document_type` flips to scontrino.\nLine items are not copied (both documents project the worksheet).\n\nNo request body. Server re-validates every convertibility guard:\ndraft only, worksheet-backed, no blocking e-invoice (past draft),\nno linked nota di credito, no ritenuta d'acconto.\n\nReturns 422 when any guard fails. The invoice is gone after a\nsuccessful conversion — navigate to the returned receipt.\n",
        "responses": {
          "201": {
            "description": "The freshly created draft scontrino (full detail shape).",
            "x-description-it": "Lo scontrino generato dalla fattura.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Receipt"
                },
                "example": {
                  "object": "receipt",
                  "id": "3d7f1b95-6c28-4a04-9e51-8b2f7a3c6d19",
                  "seq_number": 58,
                  "status": "draft",
                  "worksheet_id": "a5d21e64-8f37-4b90-ae12-3c7d6b048f95",
                  "customer_id": "4c911f08-7713-4e83-bdf6-a0692a1b43fd",
                  "total_before_tax": 233.9,
                  "total_tax": 51.46,
                  "total_after_tax": 285.36,
                  "created_at": "2026-07-17T08:00:00Z"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/invoices/{id}/issue": {
      "parameters": [
        {
          "$ref": "#/components/parameters/APIVersion"
        },
        {
          "name": "id",
          "in": "path",
          "required": true,
          "description": "Invoice id (UUID).",
          "schema": {
            "type": "string"
          }
        }
      ],
      "post": {
        "tags": [
          "Invoices"
        ],
        "summary": "Issue a draft invoice",
        "x-summary-it": "Emetti una fattura",
        "x-scope": "invoices.write",
        "x-description-it": "Porta la fattura da `draft` a `issued`, assegnandole il numero\nprogressivo del sezionale e valorizzando `issued_at`. È il punto in\ncui la fattura diventa un documento fiscale: da qui in poi non è più\neliminabile.\n\nL'emissione non trasmette allo SdI — la trasmissione non è esposta su\nquesta API.\n",
        "description": "Transitions an invoice from draft to issued, stamping\n`issued_at` and assigning the next `seq_number` for the\ncompany atomically.\n\nThe body is optional. When `issued_at` is omitted the server\nuses its own clock. Client-supplied back-dates that would\nviolate the monotonic per-company sequence return 409.\nIssuing an already-issued invoice also returns 409 (issuance\nis not idempotent — seq_number would be wasted).\n",
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/InvoiceIssueRequest"
              },
              "example": {
                "issued_at": "2026-05-29T12:00:00Z"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Invoice issued. `status` is now \"issued\".",
            "x-description-it": "La fattura emessa, con numero e data.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Invoice"
                },
                "example": {
                  "object": "invoice",
                  "id": "4412e7b9-0c35-4a18-92d7-5b6ea1f38c04",
                  "status": "issued",
                  "seq_number": 214,
                  "title": "Tagliando 60.000 km",
                  "customer_id": "4c911f08-7713-4e83-bdf6-a0692a1b43fd",
                  "customer_name": "Mario Rossi",
                  "vehicle_id": "8821f0ac-5d2b-41e7-9a10-6c3f2b7d4e55",
                  "vehicle_name": "Fiat Panda",
                  "vehicle_license": "AB123CD",
                  "worksheet_id": "a5d21e64-8f37-4b90-ae12-3c7d6b048f95",
                  "sezionale_id": "c81f4a26-7d09-4e35-b6a1-2f8c5d3e9047",
                  "sezionale_code": "A",
                  "payment_method": "bonifico",
                  "payment_due_at": "2026-08-15T00:00:00Z",
                  "total_before_tax": 233.9,
                  "total_tax": 51.46,
                  "total_after_tax": 285.36,
                  "total_payments": 150.0,
                  "outstanding_payments": 135.36,
                  "sdi_status": "delivered",
                  "issued_at": "2026-07-16T10:30:00Z",
                  "created_at": "2026-07-16T10:12:00Z",
                  "updated_at": "2026-07-16T10:30:00Z",
                  "line_items": [
                    {
                      "object": "invoice_line_item",
                      "id": "7c0f6c4e-9b1a-4c2e-9a5d-2f3b1e8a4d10",
                      "description": "Olio motore 5W30",
                      "quantity": 4,
                      "unit_type": "pz",
                      "unit_price": 18.5,
                      "vat_code": "22",
                      "tax_rate": 22,
                      "total_before_tax": 74.0,
                      "total_tax": 16.28,
                      "total_after_tax": 90.28
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/MalformedRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMediaType"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/invoices/{id}/payments": {
      "parameters": [
        {
          "$ref": "#/components/parameters/APIVersion"
        },
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        }
      ],
      "get": {
        "tags": [
          "Invoices"
        ],
        "summary": "List an invoice's payments",
        "x-summary-it": "Elenca i pagamenti della fattura",
        "x-scope": "invoices.read",
        "x-description-it": "Gli incassi registrati sulla fattura. Il residuo è\n`outstanding_payments` sulla fattura stessa.\n",
        "description": "Payments registered against the invoice (acconti/saldi), the\nsame surface as the worksheet-scoped endpoints without the\nworksheet indirection. Unpaginated.\n",
        "responses": {
          "200": {
            "description": "Payments, oldest first.",
            "x-description-it": "I pagamenti della fattura.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "object",
                    "data",
                    "has_more",
                    "url"
                  ],
                  "properties": {
                    "object": {
                      "type": "string",
                      "const": "list"
                    },
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Payment"
                      }
                    },
                    "has_more": {
                      "type": "boolean",
                      "const": false
                    },
                    "url": {
                      "type": "string"
                    }
                  }
                },
                "example": {
                  "object": "list",
                  "data": [
                    {
                      "object": "payment",
                      "id": "d47c0b58-3e91-42a6-8f05-1b9d6a3e2c74",
                      "amount": 150.0,
                      "payment_method": "MP05",
                      "payment_date": "2026-07-16",
                      "created_at": "2026-07-16T10:30:00Z"
                    }
                  ],
                  "has_more": false,
                  "url": "/v1/invoices/4412e7b9-0c35-4a18-92d7-5b6ea1f38c04/payments"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      },
      "post": {
        "tags": [
          "Invoices"
        ],
        "summary": "Register a payment on an invoice",
        "x-summary-it": "Registra un pagamento",
        "x-scope": "invoices.write",
        "x-description-it": "Registra un incasso sulla fattura. Qui `payment_method` è un codice\nministeriale (`MP01` contanti, `MP05` bonifico, `MP08` carta), a\ndifferenza del campo omonimo sulla fattura che è un'etichetta libera.\nL'importo può essere parziale.\n",
        "description": "Mirrors the web invoice dialog: the payment attaches to the\ninvoice and the invoice paid-state is refreshed.\n`payment_method` falls back to the customer's preferred method,\nthen the company default, when missing or invalid. The optional\n`financial_account_id` (conto di saldo) must belong to the\ncompany.\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PaymentCreateRequest"
              },
              "example": {
                "amount": 150.0,
                "payment_method": "MP01",
                "payment_date": "2026-07-16"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Payment registered.",
            "x-description-it": "Il pagamento registrato.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Payment"
                },
                "example": {
                  "object": "payment",
                  "id": "d47c0b58-3e91-42a6-8f05-1b9d6a3e2c74",
                  "amount": 150.0,
                  "payment_method": "MP05",
                  "payment_date": "2026-07-16",
                  "created_at": "2026-07-16T10:30:00Z"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/MalformedRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMediaType"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/invoices/{id}/payments/{paymentID}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/APIVersion"
        },
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        },
        {
          "name": "paymentID",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        }
      ],
      "delete": {
        "tags": [
          "Invoices"
        ],
        "summary": "Remove a payment from an invoice",
        "x-summary-it": "Elimina un pagamento",
        "x-scope": "invoices.write",
        "x-description-it": "Rimuove l'incasso e riporta il residuo della fattura al valore\nprecedente.\n",
        "description": "Hard-deletes the payment (payments have no soft-delete) and\nrefreshes the invoice paid-state. The payment must belong to\nthe invoice — otherwise 404.\n",
        "responses": {
          "200": {
            "description": "The removed payment.",
            "x-description-it": "Il pagamento eliminato.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Payment"
                },
                "example": {
                  "object": "payment",
                  "id": "d47c0b58-3e91-42a6-8f05-1b9d6a3e2c74",
                  "amount": 150.0,
                  "payment_method": "MP05",
                  "payment_date": "2026-07-16",
                  "created_at": "2026-07-16T10:30:00Z"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/manufacturers": {
      "parameters": [
        {
          "$ref": "#/components/parameters/APIVersion"
        }
      ],
      "get": {
        "tags": [
          "Vehicles"
        ],
        "summary": "List vehicle manufacturers",
        "x-summary-it": "Elenca le marche attive",
        "x-scope": "vehicles.read",
        "x-description-it": "Le marche selezionate dall'officina, da usare per popolare\n`manufacturer_id` in fase di creazione o modifica di un veicolo.\nL'elenco è breve e non paginato: `has_more` è sempre `false`.\n",
        "description": "Returns every vehicle manufacturer available to the\nauthenticated user's company — the global catalog plus any\ncompany-specific additions. Referenced by\n`Vehicle.manufacturer_id` on vehicle creation.\n\nIntentionally unpaginated: the set is small and changes\nrarely, so clients fetch once and filter locally in the\npicker UI. `has_more` is always `false`.\n",
        "responses": {
          "200": {
            "description": "Full manufacturer catalog.",
            "x-description-it": "Le marche attive dell'officina.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "object",
                    "data",
                    "has_more",
                    "url"
                  ],
                  "properties": {
                    "object": {
                      "type": "string",
                      "const": "list"
                    },
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Manufacturer"
                      }
                    },
                    "has_more": {
                      "type": "boolean",
                      "const": false
                    },
                    "url": {
                      "type": "string"
                    }
                  }
                },
                "example": {
                  "object": "list",
                  "data": [
                    {
                      "object": "manufacturer",
                      "id": "c3d9e8f1-4a72-4b60-8e15-9d2f7c4a6b83",
                      "name": "Fiat"
                    },
                    {
                      "object": "manufacturer",
                      "id": "9a4c2e70-6b18-4d3f-a5c9-1e8b7d0f2a46",
                      "name": "Volkswagen"
                    }
                  ],
                  "has_more": false,
                  "url": "/v1/manufacturers"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      },
      "put": {
        "tags": [
          "Vehicles"
        ],
        "summary": "Replace manufacturer",
        "x-summary-it": "Imposta le marche attive",
        "x-scope": "settings.write",
        "x-description-it": "Sostituisce l'intero insieme delle marche attive dell'officina: le\nmarche non elencate vengono disattivate. `new_names` crea al volo le\nmarche non presenti a catalogo. Almeno una marca deve restare attiva.\nRichiede il permesso `settings.write`.\n",
        "operationId": "manufacturerSet",
        "description": "company's enabled brand set; new_names creates custom brands (web\n\"new-brand\" parity) and enables them in the same call.\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "ids"
                ],
                "properties": {
                  "ids": {
                    "type": "array",
                    "description": "The complete set of manufacturer IDs that stay active.\nAnything absent is deactivated. Must not be empty.\n",
                    "items": {
                      "type": "string"
                    }
                  },
                  "new_names": {
                    "type": "array",
                    "description": "Manufacturer names not in the catalog, created on the fly\nand added to the active set. 2-60 characters each.\n",
                    "items": {
                      "type": "string"
                    }
                  }
                }
              },
              "example": {
                "ids": [
                  "c3d9e8f1-4a72-4b60-8e15-9d2f7c4a6b83",
                  "9a4c2e70-6b18-4d3f-a5c9-1e8b7d0f2a46"
                ],
                "new_names": [
                  "Ligier"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "success",
            "x-description-it": "Le marche ora attive.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/json": {
                "example": {
                  "object": "manufacturers",
                  "ids": [
                    "c3d9e8f1-4a72-4b60-8e15-9d2f7c4a6b83",
                    "9a4c2e70-6b18-4d3f-a5c9-1e8b7d0f2a46"
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/MalformedRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/manufacturers/catalog": {
      "parameters": [
        {
          "$ref": "#/components/parameters/APIVersion"
        }
      ],
      "get": {
        "tags": [
          "Vehicles"
        ],
        "summary": "Manufacturer catalog",
        "x-summary-it": "Catalogo completo delle marche",
        "x-scope": "settings.read",
        "x-description-it": "Tutte le marche disponibili, con `selected` a indicare quelle attive\nper l'officina e `vehicle_count` a indicare quanti veicoli le usano.\nÈ la vista dietro Impostazioni -\u003e Marche veicolo, quindi richiede il\npermesso `settings.read`.\n",
        "operationId": "manufacturerCatalog",
        "description": "brand catalog with the company's enabled marks and usage counts.\n",
        "responses": {
          "200": {
            "description": "success",
            "x-description-it": "Il catalogo completo delle marche.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/json": {
                "example": {
                  "object": "list",
                  "data": [
                    {
                      "object": "manufacturer_catalog_entry",
                      "id": "c3d9e8f1-4a72-4b60-8e15-9d2f7c4a6b83",
                      "name": "Fiat",
                      "selected": true,
                      "vehicle_count": 128
                    },
                    {
                      "object": "manufacturer_catalog_entry",
                      "id": "9a4c2e70-6b18-4d3f-a5c9-1e8b7d0f2a46",
                      "name": "Volkswagen",
                      "selected": true,
                      "vehicle_count": 41
                    },
                    {
                      "object": "manufacturer_catalog_entry",
                      "id": "2f6d8b31-7c05-4a92-9e4d-3b1a6c8f5d20",
                      "name": "Zastava",
                      "selected": false,
                      "vehicle_count": 0
                    }
                  ],
                  "has_more": false,
                  "url": "/v1/manufacturers/catalog"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/orders": {
      "parameters": [
        {
          "$ref": "#/components/parameters/APIVersion"
        }
      ],
      "get": {
        "tags": [
          "Suppliers"
        ],
        "summary": "List orders",
        "x-summary-it": "Elenca gli ordini a fornitore",
        "x-scope": "suppliers.read",
        "x-description-it": "Gli ordini emessi verso i fornitori, in sola lettura: creazione e\nmodifica non sono esposte su questa API.\n",
        "operationId": "orderList",
        "description": "The date filter defaults to the last 12 months; the applied\nrange is echoed back in `date_filter`. Scoped to the authenticated\nuser's company and gated on the orders.read permission.\n",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": false,
            "description": "optional substring match on supplier name / supplier\n",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "invoice",
            "in": "query",
            "required": false,
            "description": "reference (web parity)\n",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "description": "\"pending\" | \"delivery\" | \"received\" — the tab filter;\n",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "omitted",
            "in": "query",
            "required": false,
            "description": "returns every status (\"Tutti\" tab)\n",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "supplier_id",
            "in": "query",
            "required": false,
            "description": "narrow to a single supplier\n",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "sort,",
            "in": "query",
            "required": false,
            "description": "direction named sort key\n(date/number/supplier/invoice/total) +\n",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "asc|desc;",
            "in": "query",
            "required": false,
            "description": "default date desc\n",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "from,",
            "in": "query",
            "required": false,
            "description": "to YYYY-MM-DD date range bounds (on orders.created_at)\n",
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/DatePreset"
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "page size (default 20, max 100)\n",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "starting_after",
            "in": "query",
            "required": false,
            "description": "opaque cursor from a previous response\n",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "direction",
            "in": "query",
            "required": false,
            "description": "Direzione di ordinamento: `asc` o `desc`.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "from",
            "in": "query",
            "required": false,
            "description": "Inizio dell'intervallo, formato `YYYY-MM-DD`.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "sort",
            "in": "query",
            "required": false,
            "description": "Colonna di ordinamento.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "to",
            "in": "query",
            "required": false,
            "description": "Fine dell'intervallo, formato `YYYY-MM-DD`.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "+ date_filter          success",
            "x-description-it": "Gli ordini a fornitore.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ListEnvelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/Order"
                          }
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "object": "list",
                  "data": [
                    {
                      "object": "order",
                      "id": "8c14e7a2-5b30-4d96-a17f-2e9c6b4d8f05",
                      "seq_number": 214,
                      "supplier_id": "6f2b8c14-9d70-4a35-b8e2-1c5f7a3d9e60",
                      "supplier_name": "Ricambi Lombardia S.r.l.",
                      "supplier_invoice_reference": "2026/1184",
                      "status": "received",
                      "total_before_tax": 420.0,
                      "total_tax": 92.4,
                      "total_after_tax": 512.4,
                      "payment_method": "MP05",
                      "invoice_date": "2026-07-28",
                      "confirmed_at": "2026-07-20T09:00:00Z",
                      "received_at": "2026-07-27T11:30:00Z",
                      "created_at": "2026-07-18T15:00:00Z"
                    }
                  ],
                  "has_more": false,
                  "url": "/v1/orders",
                  "date_filter": {
                    "object": "date_filter",
                    "preset": "year",
                    "from": "2026-01-01",
                    "to": "2026-12-31"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/orders/status-counts": {
      "parameters": [
        {
          "$ref": "#/components/parameters/APIVersion"
        }
      ],
      "get": {
        "tags": [
          "Suppliers"
        ],
        "summary": "Order status counts",
        "x-summary-it": "Conta gli ordini per stato",
        "x-scope": "suppliers.read",
        "x-description-it": "Il numero di ordini in ciascuno stato, con gli stessi filtri\ndell'elenco.\n",
        "operationId": "orderStatusCounts",
        "description": "tab counters for the \"Arrivi Merce\" list. Counts respect the date\nrange and the free-text search (supplier narrowing is not applied to the\ncounts, web parity). The applied range is echoed in date_filter.\n",
        "parameters": [
          {
            "name": "from",
            "in": "query",
            "required": false,
            "description": "Inizio dell'intervallo, formato `YYYY-MM-DD`.",
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/DatePreset"
          },
          {
            "name": "q",
            "in": "query",
            "required": false,
            "description": "Ricerca testuale libera.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "to",
            "in": "query",
            "required": false,
            "description": "Fine dell'intervallo, formato `YYYY-MM-DD`.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "success",
            "x-description-it": "Il numero di ordini per stato.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OrderStatusCounts"
                },
                "example": {
                  "object": "order_status_counts",
                  "pending": 2,
                  "delivery": 5,
                  "received": 118,
                  "date_filter": {
                    "object": "date_filter",
                    "preset": "year",
                    "from": "2026-01-01",
                    "to": "2026-12-31"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/price-lists": {
      "parameters": [
        {
          "$ref": "#/components/parameters/APIVersion"
        }
      ],
      "get": {
        "tags": [
          "Settings"
        ],
        "summary": "List price lists",
        "x-summary-it": "Elenca i listini",
        "x-scope": "settings.read",
        "x-description-it": "I listini configurati, applicabili ai clienti per ottenere prezzi\ndedicati.\n",
        "operationId": "priceListList",
        "responses": {
          "200": {
            "description": "success",
            "x-description-it": "I listini configurati.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/json": {
                "example": {
                  "object": "list",
                  "data": [
                    {
                      "object": "price_list",
                      "id": "9c5e1a83-6b24-4d70-8f39-2a7c4e1b8d56",
                      "name": "Listino flotte"
                    }
                  ],
                  "has_more": false,
                  "url": "/v1/price-lists"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      },
      "post": {
        "tags": [
          "Settings"
        ],
        "summary": "Create a price list",
        "x-summary-it": "Crea un listino",
        "x-scope": "settings.write",
        "x-description-it": "Crea un listino prezzi.\n",
        "operationId": "priceListCreate",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "name"
                ],
                "properties": {
                  "name": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "name": "Nuovo nome"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "success",
            "x-description-it": "Il listino creato.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/json": {
                "example": {
                  "object": "price_list",
                  "id": "9c5e1a83-6b24-4d70-8f39-2a7c4e1b8d56",
                  "name": "Listino flotte"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/price-lists/{id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/APIVersion"
        },
        {
          "name": "id",
          "in": "path",
          "required": true,
          "description": "Identifier of the target resource.",
          "schema": {
            "type": "string"
          }
        }
      ],
      "delete": {
        "tags": [
          "Settings"
        ],
        "summary": "Delete price list",
        "x-summary-it": "Elimina un listino",
        "x-scope": "settings.write",
        "x-description-it": "Elimina il listino. I clienti che lo usavano tornano ai prezzi di\nlistino base.\n",
        "operationId": "priceListDelete",
        "responses": {
          "200": {
            "description": "success",
            "x-description-it": "Il listino eliminato.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/json": {
                "example": {
                  "object": "price_list",
                  "id": "9c5e1a83-6b24-4d70-8f39-2a7c4e1b8d56",
                  "name": "Listino flotte"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      },
      "patch": {
        "tags": [
          "Settings"
        ],
        "summary": "Update price list",
        "x-summary-it": "Aggiorna un listino",
        "x-scope": "settings.write",
        "x-description-it": "Aggiorna il nome del listino.\n",
        "operationId": "priceListPatch",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "name"
                ],
                "properties": {
                  "name": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "name": "Nuovo nome"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "success",
            "x-description-it": "Il listino aggiornato.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/json": {
                "example": {
                  "object": "price_list",
                  "id": "9c5e1a83-6b24-4d70-8f39-2a7c4e1b8d56",
                  "name": "Listino flotte"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/quotes": {
      "parameters": [
        {
          "$ref": "#/components/parameters/APIVersion"
        }
      ],
      "get": {
        "tags": [
          "Quotes"
        ],
        "summary": "List quotes",
        "x-summary-it": "Elenca i preventivi",
        "x-scope": "quotes.read",
        "x-description-it": "I preventivi dell'officina, dal più recente. Filtra per stato con\n`status`, per cliente con `customer_id`, per veicolo con `vehicle_id`\ne per periodo con `from` / `to`. La paginazione usa un cursore opaco:\npassa `next_starting_after` invariato in `starting_after` e fermati\nquando `has_more` è `false`.\n",
        "description": "Returns quotes in the authenticated user's company, newest\nfirst. Filter by status with `?status=`, search by customer\nor title with `?q=`, and bound the result by `created_at`\nwith `?from=` / `?to=` / `?preset=`. Paginate with cursor-based\n`?limit=` + `?starting_after=`.\n\nThe date window defaults to the last 12 months; set it with\n`from`/`to`/`preset`. The applied range is echoed in\n`date_filter`.\n\nList rows omit `line_items` to keep the payload small —\nfetch the detail endpoint when you need the full body.\n",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": false,
            "description": "Substring match against customer / title.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "description": "Filter to a single lifecycle state.",
            "schema": {
              "type": "string",
              "enum": [
                "waiting",
                "accepted",
                "rejected"
              ]
            }
          },
          {
            "name": "sort",
            "in": "query",
            "required": false,
            "description": "Sort key (default `date` = creation date). Named keys are\nmapped server-side to whitelisted columns.\n",
            "schema": {
              "type": "string",
              "enum": [
                "date",
                "number",
                "customer",
                "vehicle",
                "total"
              ]
            }
          },
          {
            "name": "direction",
            "in": "query",
            "required": false,
            "description": "Sort direction (default `desc`).",
            "schema": {
              "type": "string",
              "enum": [
                "asc",
                "desc"
              ]
            }
          },
          {
            "name": "from",
            "in": "query",
            "required": false,
            "description": "Inclusive lower bound on `created_at` (YYYY-MM-DD).",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "to",
            "in": "query",
            "required": false,
            "description": "Inclusive upper bound on `created_at` (YYYY-MM-DD).",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "$ref": "#/components/parameters/DatePreset"
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Page size. Default 20, max 100.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 20
            }
          },
          {
            "name": "starting_after",
            "in": "query",
            "required": false,
            "description": "Opaque cursor from a prior response. Omit on the first page.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Page of quotes (without line items).",
            "x-description-it": "Una pagina di preventivi.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ListEnvelope"
                    },
                    {
                      "type": "object",
                      "required": [
                        "date_filter"
                      ],
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/Quote"
                          }
                        },
                        "date_filter": {
                          "$ref": "#/components/schemas/DateFilter"
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "object": "list",
                  "data": [
                    {
                      "object": "quote",
                      "id": "b93f7c25-4a18-4e60-9d37-8c1b5f2a6e04",
                      "seq_number": 87,
                      "status": "waiting",
                      "title": "Preventivo tagliando",
                      "customer_id": "4c911f08-7713-4e83-bdf6-a0692a1b43fd",
                      "customer_name": "Mario Rossi",
                      "vehicle_id": "8821f0ac-5d2b-41e7-9a10-6c3f2b7d4e55",
                      "vehicle_name": "Fiat Panda",
                      "vehicle_license": "AB123CD",
                      "work_category_id": null,
                      "work_category_name": null,
                      "km": 61240,
                      "worksheet_id": null,
                      "worksheet_seq_number": null,
                      "total_before_tax": 70.0,
                      "total_tax": 15.4,
                      "total_after_tax": 85.4,
                      "total_discount": 0,
                      "hide_discounts": false,
                      "created_at": "2026-07-10T09:14:00Z",
                      "updated_at": "2026-07-10T09:20:00Z"
                    }
                  ],
                  "has_more": true,
                  "next_starting_after": "eyJvIjoyMH0",
                  "url": "/v1/quotes",
                  "date_filter": {
                    "object": "date_filter",
                    "preset": "year",
                    "from": "2026-01-01",
                    "to": "2026-12-31"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMediaType"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      },
      "post": {
        "tags": [
          "Quotes"
        ],
        "summary": "Create a quote",
        "x-summary-it": "Crea un preventivo",
        "x-scope": "quotes.write",
        "x-description-it": "Crea un preventivo in stato `waiting`. Il numero progressivo\n(`seq_number`) è assegnato dal server. Le righe non si passano qui:\ncrea il preventivo, poi aggiungile con\n`POST /v1/quotes/{id}/line-items/batch`.\n",
        "description": "Creates a new quote owned by the authenticated user's\ncompany. Server-managed fields (`id`, `company_id`,\n`created_by`, `seq_number`, `status`, all timestamps,\ntotals) are filled in automatically. Status starts as\n`waiting`. Line items are not accepted at creation time.\n\nForeign-key references in the body (`customer_id`, `vehicle_id`,\n`work_category_id`) must resolve to resources owned by the\nauthenticated user's company. Unreachable refs return 422\n`parameter_invalid` pointing at the offending field.\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/QuoteCreateRequest"
              },
              "example": {
                "customer_id": "4c911f08-7713-4e83-bdf6-a0692a1b43fd",
                "vehicle_id": "8821f0ac-5d2b-41e7-9a10-6c3f2b7d4e55",
                "title": "Preventivo tagliando",
                "km": 61240
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Quote created.",
            "x-description-it": "Il preventivo creato.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Quote"
                },
                "example": {
                  "object": "quote",
                  "id": "b93f7c25-4a18-4e60-9d37-8c1b5f2a6e04",
                  "seq_number": 87,
                  "status": "waiting",
                  "title": "Preventivo tagliando",
                  "customer_id": "4c911f08-7713-4e83-bdf6-a0692a1b43fd",
                  "customer_name": "Mario Rossi",
                  "vehicle_id": "8821f0ac-5d2b-41e7-9a10-6c3f2b7d4e55",
                  "vehicle_name": "Fiat Panda",
                  "vehicle_license": "AB123CD",
                  "work_category_id": null,
                  "work_category_name": null,
                  "km": 61240,
                  "worksheet_id": null,
                  "worksheet_seq_number": null,
                  "total_before_tax": 70.0,
                  "total_tax": 15.4,
                  "total_after_tax": 85.4,
                  "total_discount": 0,
                  "hide_discounts": false,
                  "created_at": "2026-07-10T09:14:00Z",
                  "updated_at": "2026-07-10T09:20:00Z"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/MalformedRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMediaType"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/quotes/status-counts": {
      "parameters": [
        {
          "$ref": "#/components/parameters/APIVersion"
        }
      ],
      "get": {
        "tags": [
          "Quotes"
        ],
        "summary": "Count quotes per status",
        "x-summary-it": "Conta i preventivi per stato",
        "x-scope": "quotes.read",
        "x-description-it": "Il numero di preventivi in ciascuno stato, con gli stessi filtri\ndell'elenco. Serve a popolare i contatori senza scaricare i\npreventivi.\n",
        "description": "Returns how many quotes sit in each status, for the tab counters\nabove the mobile list. Accepts the same filters as\n`GET /v1/quotes` minus `status` and pagination (`q`,\n`from`/`to`/`preset` with the same date-filter semantics), so\nthe numbers always match what the list shows.\nThe applied range is echoed in `date_filter`.\n",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": false,
            "description": "Substring match against customer or vehicle name.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "from",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "to",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "$ref": "#/components/parameters/DatePreset"
          }
        ],
        "responses": {
          "200": {
            "description": "Counts per status.",
            "x-description-it": "Il numero di preventivi per stato.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/QuoteStatusCounts"
                },
                "example": {
                  "object": "quote_status_counts",
                  "waiting": 6,
                  "accepted": 23,
                  "rejected": 4,
                  "date_filter": {
                    "object": "date_filter",
                    "preset": "year",
                    "from": "2026-01-01",
                    "to": "2026-12-31"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/quotes/{id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/APIVersion"
        },
        {
          "name": "id",
          "in": "path",
          "required": true,
          "description": "Quote id (UUID).",
          "schema": {
            "type": "string"
          }
        }
      ],
      "get": {
        "tags": [
          "Quotes"
        ],
        "summary": "Retrieve a quote",
        "x-summary-it": "Recupera un preventivo",
        "x-scope": "quotes.read",
        "x-description-it": "Restituisce il preventivo con le righe (`line_items`) e i totali\ncalcolati. Se il preventivo è già stato convertito, `worksheet_id`\npunta alla scheda lavoro generata.\n",
        "description": "Returns a single quote (with embedded line items) by id,\nscoped to the authenticated user's company. Unknown ids,\nids from another company, and soft-deleted rows all return\n404 — never 403.\n",
        "responses": {
          "200": {
            "description": "Quote found. Includes `line_items`.",
            "x-description-it": "Il preventivo richiesto.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Quote"
                },
                "example": {
                  "object": "quote",
                  "id": "b93f7c25-4a18-4e60-9d37-8c1b5f2a6e04",
                  "seq_number": 87,
                  "status": "waiting",
                  "title": "Preventivo tagliando",
                  "customer_id": "4c911f08-7713-4e83-bdf6-a0692a1b43fd",
                  "customer_name": "Mario Rossi",
                  "vehicle_id": "8821f0ac-5d2b-41e7-9a10-6c3f2b7d4e55",
                  "vehicle_name": "Fiat Panda",
                  "vehicle_license": "AB123CD",
                  "work_category_id": null,
                  "work_category_name": null,
                  "km": 61240,
                  "worksheet_id": null,
                  "worksheet_seq_number": null,
                  "total_before_tax": 70.0,
                  "total_tax": 15.4,
                  "total_after_tax": 85.4,
                  "total_discount": 0,
                  "hide_discounts": false,
                  "created_at": "2026-07-10T09:14:00Z",
                  "updated_at": "2026-07-10T09:20:00Z",
                  "comments": "Valido 30 giorni.",
                  "line_items": [
                    {
                      "object": "quote_line_item",
                      "id": "2e6b8d40-7f19-4c53-a802-9d4e1b7c3f56",
                      "article_id": null,
                      "description": "Manodopera preventivata",
                      "quantity": 2,
                      "unit_type": "pz",
                      "unit_price": 35.0,
                      "unit_discount_rate": 0,
                      "tax_rate": 22,
                      "vat_code": "22",
                      "total_before_tax": 70.0,
                      "total_tax": 15.4,
                      "total_after_tax": 85.4,
                      "position": 1,
                      "created_at": "2026-07-10T09:20:00Z"
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMediaType"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      },
      "patch": {
        "tags": [
          "Quotes"
        ],
        "summary": "Update quote metadata",
        "x-summary-it": "Aggiorna un preventivo",
        "x-scope": "quotes.write",
        "x-description-it": "Aggiorna solo i campi presenti nel corpo: gli altri restano\ninvariati, e `null` svuota il campo. Per cambiare stato usa\n`POST /v1/quotes/{id}/transitions`, non questo endpoint.\n",
        "description": "Three-state PATCH semantics (omitted = unchanged, `null` =\nclear, value = set). Status transitions go through\n`POST /v1/quotes/{id}/transitions`, not PATCH.\n\nFields not listed in `QuotePatchRequest` are intentionally\nNOT patchable: `id`, `company_id`, `created_by`,\n`seq_number`, `status`, `customer_id`, `vehicle_id`, all\ntotals, all timestamps, `worksheet_id`, `line_items`.\n\nForeign-key references in the body (`work_category_id`) must\nresolve to resources owned by the authenticated user's company.\nUnreachable refs return 422 `parameter_invalid` pointing at the\noffending field.\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/QuotePatchRequest"
              },
              "example": {
                "title": "Preventivo aggiornato",
                "comments": null
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Quote updated. Returns the full post-update entity.",
            "x-description-it": "Il preventivo aggiornato.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Quote"
                },
                "example": {
                  "object": "quote",
                  "id": "b93f7c25-4a18-4e60-9d37-8c1b5f2a6e04",
                  "seq_number": 87,
                  "status": "waiting",
                  "title": "Preventivo tagliando",
                  "customer_id": "4c911f08-7713-4e83-bdf6-a0692a1b43fd",
                  "customer_name": "Mario Rossi",
                  "vehicle_id": "8821f0ac-5d2b-41e7-9a10-6c3f2b7d4e55",
                  "vehicle_name": "Fiat Panda",
                  "vehicle_license": "AB123CD",
                  "work_category_id": null,
                  "work_category_name": null,
                  "km": 61240,
                  "worksheet_id": null,
                  "worksheet_seq_number": null,
                  "total_before_tax": 70.0,
                  "total_tax": 15.4,
                  "total_after_tax": 85.4,
                  "total_discount": 0,
                  "hide_discounts": false,
                  "created_at": "2026-07-10T09:14:00Z",
                  "updated_at": "2026-07-10T09:20:00Z",
                  "comments": "Valido 30 giorni.",
                  "line_items": [
                    {
                      "object": "quote_line_item",
                      "id": "2e6b8d40-7f19-4c53-a802-9d4e1b7c3f56",
                      "article_id": null,
                      "description": "Manodopera preventivata",
                      "quantity": 2,
                      "unit_type": "pz",
                      "unit_price": 35.0,
                      "unit_discount_rate": 0,
                      "tax_rate": 22,
                      "vat_code": "22",
                      "total_before_tax": 70.0,
                      "total_tax": 15.4,
                      "total_after_tax": 85.4,
                      "position": 1,
                      "created_at": "2026-07-10T09:20:00Z"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/MalformedRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMediaType"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      },
      "delete": {
        "tags": [
          "Quotes"
        ],
        "summary": "Soft-delete a quote",
        "x-summary-it": "Elimina un preventivo",
        "x-scope": "quotes.write",
        "x-description-it": "Eliminazione logica: il preventivo sparisce dagli elenchi. La\nrisposta contiene il preventivo con `deleted_at` valorizzato.\n",
        "description": "Marks the quote as deleted by stamping `deleted_at` with the\nserver clock. Returns the deleted resource (200) for shape\nconsistency.\n",
        "responses": {
          "200": {
            "description": "Quote soft-deleted. `deleted_at` is populated.",
            "x-description-it": "Il preventivo eliminato.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Quote"
                },
                "example": {
                  "object": "quote",
                  "id": "b93f7c25-4a18-4e60-9d37-8c1b5f2a6e04",
                  "seq_number": 87,
                  "status": "waiting",
                  "title": "Preventivo tagliando",
                  "customer_id": "4c911f08-7713-4e83-bdf6-a0692a1b43fd",
                  "customer_name": "Mario Rossi",
                  "vehicle_id": "8821f0ac-5d2b-41e7-9a10-6c3f2b7d4e55",
                  "vehicle_name": "Fiat Panda",
                  "vehicle_license": "AB123CD",
                  "work_category_id": null,
                  "work_category_name": null,
                  "km": 61240,
                  "worksheet_id": null,
                  "worksheet_seq_number": null,
                  "total_before_tax": 70.0,
                  "total_tax": 15.4,
                  "total_after_tax": 85.4,
                  "total_discount": 0,
                  "hide_discounts": false,
                  "created_at": "2026-07-10T09:14:00Z",
                  "updated_at": "2026-07-10T09:20:00Z",
                  "deleted_at": "2026-07-20T11:04:00Z"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/quotes/{id}/convert": {
      "parameters": [
        {
          "$ref": "#/components/parameters/APIVersion"
        },
        {
          "name": "id",
          "in": "path",
          "required": true,
          "description": "Quote id (UUID).",
          "schema": {
            "type": "string"
          }
        }
      ],
      "post": {
        "tags": [
          "Quotes"
        ],
        "summary": "Convert into a scheda lavoro",
        "x-summary-it": "Converti in scheda lavoro",
        "x-scope": "quotes.write",
        "x-description-it": "Genera una scheda lavoro dal preventivo, copiandone righe e totali,\ne collega i due documenti (`worksheet_id` sul preventivo).\n\nAttenzione: la risposta è la **scheda lavoro** creata, non il\npreventivo — l'oggetto restituito ha `object: worksheet`. La\nconversione è una sola volta: un preventivo già convertito risponde\n`409`.\n",
        "description": "Creates a worksheet carrying the quote's customer, vehicle, km,\ntitle, notes, line items and totals — the same conversion the web\nperforms — then links the quote to it (which also marks the quote\n`accepted`). The quote is kept. A quote already linked to a\nworksheet returns 422; when the company requires km on\nvehicle-linked worksheets and the quote has none, 422\n`parameter_missing` `/km`.\n\nNo request body.\n",
        "responses": {
          "201": {
            "description": "The freshly created scheda lavoro (full detail shape).",
            "x-description-it": "La scheda lavoro generata dal preventivo.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Worksheet"
                },
                "example": {
                  "object": "worksheet",
                  "id": "a5d21e64-8f37-4b90-ae12-3c7d6b048f95",
                  "seq_number": 412,
                  "status": "draft",
                  "title": "Preventivo tagliando",
                  "customer_id": "4c911f08-7713-4e83-bdf6-a0692a1b43fd",
                  "vehicle_id": "8821f0ac-5d2b-41e7-9a10-6c3f2b7d4e55",
                  "total_before_tax": 70.0,
                  "total_tax": 15.4,
                  "total_after_tax": 85.4,
                  "frozen": false,
                  "created_at": "2026-07-11T08:00:00Z"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMediaType"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/quotes/{id}/line-items/batch": {
      "parameters": [
        {
          "$ref": "#/components/parameters/APIVersion"
        },
        {
          "name": "id",
          "in": "path",
          "required": true,
          "description": "Quote id (UUID).",
          "schema": {
            "type": "string"
          }
        }
      ],
      "post": {
        "tags": [
          "Quotes"
        ],
        "summary": "Mutate quote line items in a batch",
        "x-summary-it": "Modifica le righe in blocco",
        "x-scope": "quotes.write",
        "x-description-it": "Aggiunge, aggiorna, rimuove e riordina le righe in una sola\nrichiesta, applicata come singola transazione: se una operazione\nfallisce non viene applicata nessuna. I totali del preventivo vengono\nricalcolati e restituiti.\n",
        "description": "Adds, updates, removes, and reorders the quote's line items in a\nsingle atomic diff, then returns the full quote with recomputed\ntotals and the updated `line_items` array. The top-level quote\nPATCH is metadata-only; all line-item mutation flows through this\nendpoint. Identical wire shape to the worksheet batch endpoint.\n\n**Operations** (all arrays optional; empty body is a no-op):\n`add`, `update` (full replace by id), `remove` (by id),\n`reorder`. An id may appear in at most one of\n`remove`/`update`/`reorder`. As with worksheets, `update` is a\nremove+add internally so an updated line receives a NEW id;\n`tax_rate` is resolved server-side from `vat_code`.\n\nQuotes are estimates — there is no invoice-freeze rule and\narticle references on lines do not move inventory stock (they\nare validated for ownership only).\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/LineItemsBatchRequest"
              },
              "example": {
                "add": [
                  {
                    "description": "Estimated labour",
                    "quantity": 2,
                    "unit_type": "pz",
                    "unit_price": 35,
                    "unit_discount_rate": 0,
                    "vat_code": "22"
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Batch applied. Returns the updated quote (with `line_items`).",
            "x-description-it": "Il preventivo con le righe aggiornate e i totali ricalcolati.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Quote"
                },
                "example": {
                  "object": "quote",
                  "id": "b93f7c25-4a18-4e60-9d37-8c1b5f2a6e04",
                  "seq_number": 87,
                  "status": "waiting",
                  "title": "Preventivo tagliando",
                  "customer_id": "4c911f08-7713-4e83-bdf6-a0692a1b43fd",
                  "customer_name": "Mario Rossi",
                  "vehicle_id": "8821f0ac-5d2b-41e7-9a10-6c3f2b7d4e55",
                  "vehicle_name": "Fiat Panda",
                  "vehicle_license": "AB123CD",
                  "work_category_id": null,
                  "work_category_name": null,
                  "km": 61240,
                  "worksheet_id": null,
                  "worksheet_seq_number": null,
                  "total_before_tax": 70.0,
                  "total_tax": 15.4,
                  "total_after_tax": 85.4,
                  "total_discount": 0,
                  "hide_discounts": false,
                  "created_at": "2026-07-10T09:14:00Z",
                  "updated_at": "2026-07-10T09:20:00Z",
                  "comments": "Valido 30 giorni.",
                  "line_items": [
                    {
                      "object": "quote_line_item",
                      "id": "2e6b8d40-7f19-4c53-a802-9d4e1b7c3f56",
                      "article_id": null,
                      "description": "Manodopera preventivata",
                      "quantity": 2,
                      "unit_type": "pz",
                      "unit_price": 35.0,
                      "unit_discount_rate": 0,
                      "tax_rate": 22,
                      "vat_code": "22",
                      "total_before_tax": 70.0,
                      "total_tax": 15.4,
                      "total_after_tax": 85.4,
                      "position": 1,
                      "created_at": "2026-07-10T09:20:00Z"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/MalformedRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMediaType"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/quotes/{id}/transitions": {
      "parameters": [
        {
          "$ref": "#/components/parameters/APIVersion"
        },
        {
          "name": "id",
          "in": "path",
          "required": true,
          "description": "Quote id (UUID).",
          "schema": {
            "type": "string"
          }
        }
      ],
      "post": {
        "tags": [
          "Quotes"
        ],
        "summary": "Apply a status transition",
        "x-summary-it": "Cambia stato al preventivo",
        "x-scope": "quotes.write",
        "x-description-it": "Porta il preventivo in `waiting`, `accepted` o `rejected`. È il modo\ncorretto di registrare l'esito: non impostare `status` via PATCH.\nAccettare un preventivo non crea da solo la scheda lavoro — per\nquello serve `POST /v1/quotes/{id}/convert`.\n",
        "description": "Drives the quote lifecycle: `waiting` → `accepted` /\n`rejected`. Any other `action` value returns 422.\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/QuoteTransitionRequest"
              },
              "example": {
                "action": "accepted"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Transition applied. Returns the post-transition entity.",
            "x-description-it": "Il preventivo dopo la transizione.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Quote"
                },
                "example": {
                  "object": "quote",
                  "id": "b93f7c25-4a18-4e60-9d37-8c1b5f2a6e04",
                  "seq_number": 87,
                  "status": "accepted",
                  "title": "Preventivo tagliando",
                  "customer_id": "4c911f08-7713-4e83-bdf6-a0692a1b43fd",
                  "customer_name": "Mario Rossi",
                  "vehicle_id": "8821f0ac-5d2b-41e7-9a10-6c3f2b7d4e55",
                  "vehicle_name": "Fiat Panda",
                  "vehicle_license": "AB123CD",
                  "work_category_id": null,
                  "work_category_name": null,
                  "km": 61240,
                  "worksheet_id": null,
                  "worksheet_seq_number": null,
                  "total_before_tax": 70.0,
                  "total_tax": 15.4,
                  "total_after_tax": 85.4,
                  "total_discount": 0,
                  "hide_discounts": false,
                  "created_at": "2026-07-10T09:14:00Z",
                  "updated_at": "2026-07-10T09:20:00Z",
                  "comments": "Valido 30 giorni.",
                  "line_items": [
                    {
                      "object": "quote_line_item",
                      "id": "2e6b8d40-7f19-4c53-a802-9d4e1b7c3f56",
                      "article_id": null,
                      "description": "Manodopera preventivata",
                      "quantity": 2,
                      "unit_type": "pz",
                      "unit_price": 35.0,
                      "unit_discount_rate": 0,
                      "tax_rate": 22,
                      "vat_code": "22",
                      "total_before_tax": 70.0,
                      "total_tax": 15.4,
                      "total_after_tax": 85.4,
                      "position": 1,
                      "created_at": "2026-07-10T09:20:00Z"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/MalformedRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMediaType"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/receipts": {
      "parameters": [
        {
          "$ref": "#/components/parameters/APIVersion"
        }
      ],
      "get": {
        "tags": [
          "Receipts"
        ],
        "summary": "List receipts (date range, ?q= customer filter, unpaginated)",
        "x-summary-it": "Elenca gli scontrini",
        "x-scope": "receipts.read",
        "x-description-it": "Gli scontrini dell'officina, dal più recente. Filtra per cliente con\n`q` e per periodo con `from` / `to`.\n\nA differenza degli altri elenchi questo non è paginato: restituisce\ntutti gli scontrini del periodo e `has_more` è sempre `false`.\n",
        "parameters": [
          {
            "name": "from",
            "in": "query",
            "required": false,
            "description": "Inizio dell'intervallo, formato `YYYY-MM-DD`.",
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/DatePreset"
          },
          {
            "name": "q",
            "in": "query",
            "required": false,
            "description": "Ricerca testuale libera.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "sdi_status",
            "in": "query",
            "required": false,
            "description": "Filtra per stato di trasmissione allo SdI.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "description": "Filtra per stato.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "to",
            "in": "query",
            "required": false,
            "description": "Fine dell'intervallo, formato `YYYY-MM-DD`.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Receipts + date_filter.",
            "x-description-it": "Una pagina di scontrini.",
            "content": {
              "application/json": {
                "example": {
                  "object": "list",
                  "data": [
                    {
                      "object": "receipt",
                      "id": "3d7f1b95-6c28-4a04-9e51-8b2f7a3c6d19",
                      "type": "issued",
                      "status": "sale",
                      "seq_number": 58,
                      "title": "Tagliando 60.000 km",
                      "customer_id": "4c911f08-7713-4e83-bdf6-a0692a1b43fd",
                      "customer_name": "Mario Rossi",
                      "vehicle_id": "8821f0ac-5d2b-41e7-9a10-6c3f2b7d4e55",
                      "vehicle_name": "Fiat Panda",
                      "vehicle_license": "AB123CD",
                      "worksheet_id": "a5d21e64-8f37-4b90-ae12-3c7d6b048f95",
                      "sezionale_id": "c81f4a26-7d09-4e35-b6a1-2f8c5d3e9047",
                      "sezionale_code": "B",
                      "payment_method": "MP01",
                      "total_before_tax": 233.9,
                      "total_tax": 51.46,
                      "total_after_tax": 285.36,
                      "total_payments": 285.36,
                      "outstanding_payments": 0,
                      "electronic_receipts_enabled": false,
                      "e_receipt_status": null,
                      "issued_at": "2026-07-17T08:20:00Z",
                      "voided_at": null,
                      "created_at": "2026-07-17T08:00:00Z",
                      "updated_at": "2026-07-17T08:20:00Z"
                    }
                  ],
                  "has_more": true,
                  "next_starting_after": "eyJvIjoyMH0",
                  "url": "/v1/receipts",
                  "date_filter": {
                    "object": "date_filter",
                    "preset": "year",
                    "from": "2026-01-01",
                    "to": "2026-12-31"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      },
      "post": {
        "tags": [
          "Receipts"
        ],
        "summary": "Create a receipt from a worksheet",
        "x-summary-it": "Crea uno scontrino",
        "x-scope": "receipts.write",
        "x-description-it": "Lo scontrino nasce sempre da una scheda lavoro: passa `worksheet_id`\ne righe, totali e anagrafiche vengono copiati da lì.\n\nUna scheda lavoro può generare un solo scontrino: se ne esiste già\nuno la richiesta risponde `409`.\n",
        "description": "Body {worksheet_id, payment_method}. 409 when the worksheet already has a receipt.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "worksheet_id",
                  "payment_method"
                ],
                "properties": {
                  "worksheet_id": {
                    "type": "string",
                    "description": "La scheda lavoro da cui generare lo scontrino."
                  },
                  "payment_method": {
                    "type": "string",
                    "description": "Codice ministeriale, es. `MP01` contanti."
                  }
                }
              },
              "example": {
                "worksheet_id": "a5d21e64-8f37-4b90-ae12-3c7d6b048f95",
                "payment_method": "MP01"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Receipt (line items included).",
            "x-description-it": "Lo scontrino creato, in bozza.",
            "content": {
              "application/json": {
                "example": {
                  "object": "receipt",
                  "id": "3d7f1b95-6c28-4a04-9e51-8b2f7a3c6d19",
                  "type": "draft",
                  "status": "sale",
                  "title": "Tagliando 60.000 km",
                  "customer_id": "4c911f08-7713-4e83-bdf6-a0692a1b43fd",
                  "customer_name": "Mario Rossi",
                  "vehicle_id": "8821f0ac-5d2b-41e7-9a10-6c3f2b7d4e55",
                  "vehicle_name": "Fiat Panda",
                  "vehicle_license": "AB123CD",
                  "worksheet_id": "a5d21e64-8f37-4b90-ae12-3c7d6b048f95",
                  "sezionale_id": "c81f4a26-7d09-4e35-b6a1-2f8c5d3e9047",
                  "sezionale_code": "B",
                  "payment_method": "MP01",
                  "total_before_tax": 233.9,
                  "total_tax": 51.46,
                  "total_after_tax": 285.36,
                  "total_payments": 285.36,
                  "outstanding_payments": 0,
                  "electronic_receipts_enabled": false,
                  "e_receipt_status": null,
                  "voided_at": null,
                  "created_at": "2026-07-17T08:00:00Z",
                  "updated_at": "2026-07-17T08:20:00Z",
                  "seq_number": null,
                  "issued_at": null
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/MalformedRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/receipts/{id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/APIVersion"
        },
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        }
      ],
      "get": {
        "tags": [
          "Receipts"
        ],
        "summary": "Retrieve a receipt (line items included)",
        "x-summary-it": "Recupera uno scontrino",
        "x-scope": "receipts.read",
        "x-description-it": "Restituisce lo scontrino con le righe (`line_items`) e i totali. Il\ncampo `type` è derivato e vale `draft`, `issued` o `voided`.\n",
        "responses": {
          "200": {
            "description": "Receipt.",
            "x-description-it": "Lo scontrino richiesto.",
            "content": {
              "application/json": {
                "example": {
                  "object": "receipt",
                  "id": "3d7f1b95-6c28-4a04-9e51-8b2f7a3c6d19",
                  "type": "issued",
                  "status": "sale",
                  "seq_number": 58,
                  "title": "Tagliando 60.000 km",
                  "customer_id": "4c911f08-7713-4e83-bdf6-a0692a1b43fd",
                  "customer_name": "Mario Rossi",
                  "vehicle_id": "8821f0ac-5d2b-41e7-9a10-6c3f2b7d4e55",
                  "vehicle_name": "Fiat Panda",
                  "vehicle_license": "AB123CD",
                  "worksheet_id": "a5d21e64-8f37-4b90-ae12-3c7d6b048f95",
                  "sezionale_id": "c81f4a26-7d09-4e35-b6a1-2f8c5d3e9047",
                  "sezionale_code": "B",
                  "payment_method": "MP01",
                  "total_before_tax": 233.9,
                  "total_tax": 51.46,
                  "total_after_tax": 285.36,
                  "total_payments": 285.36,
                  "outstanding_payments": 0,
                  "electronic_receipts_enabled": false,
                  "e_receipt_status": null,
                  "issued_at": "2026-07-17T08:20:00Z",
                  "voided_at": null,
                  "created_at": "2026-07-17T08:00:00Z",
                  "updated_at": "2026-07-17T08:20:00Z",
                  "line_items": [
                    {
                      "object": "receipt_line_item",
                      "id": "7c0f6c4e-9b1a-4c2e-9a5d-2f3b1e8a4d10",
                      "description": "Olio motore 5W30",
                      "quantity": 4,
                      "unit_type": "pz",
                      "unit_price": 18.5,
                      "vat_code": "22",
                      "tax_rate": 22,
                      "total_price": 74.0
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      },
      "patch": {
        "tags": [
          "Receipts"
        ],
        "summary": "Update a draft receipt (title, comments, email, payment_method, sezionale_id)",
        "x-summary-it": "Aggiorna uno scontrino",
        "x-scope": "receipts.write",
        "x-description-it": "Modifica i campi descrittivi di uno scontrino ancora in bozza.\nAggiorna solo i campi presenti nel corpo: gli altri restano\ninvariati.\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "title": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "comments": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "internal_comments": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "email": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "payment_method": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "sezionale_id": {
                    "type": [
                      "string",
                      "null"
                    ]
                  }
                }
              },
              "example": {
                "title": "Scontrino tagliando",
                "payment_method": "MP08"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated receipt.",
            "x-description-it": "Lo scontrino aggiornato.",
            "content": {
              "application/json": {
                "example": {
                  "object": "receipt",
                  "id": "3d7f1b95-6c28-4a04-9e51-8b2f7a3c6d19",
                  "type": "draft",
                  "status": "sale",
                  "title": "Tagliando 60.000 km",
                  "customer_id": "4c911f08-7713-4e83-bdf6-a0692a1b43fd",
                  "customer_name": "Mario Rossi",
                  "vehicle_id": "8821f0ac-5d2b-41e7-9a10-6c3f2b7d4e55",
                  "vehicle_name": "Fiat Panda",
                  "vehicle_license": "AB123CD",
                  "worksheet_id": "a5d21e64-8f37-4b90-ae12-3c7d6b048f95",
                  "sezionale_id": "c81f4a26-7d09-4e35-b6a1-2f8c5d3e9047",
                  "sezionale_code": "B",
                  "payment_method": "MP01",
                  "total_before_tax": 233.9,
                  "total_tax": 51.46,
                  "total_after_tax": 285.36,
                  "total_payments": 285.36,
                  "outstanding_payments": 0,
                  "electronic_receipts_enabled": false,
                  "e_receipt_status": null,
                  "voided_at": null,
                  "created_at": "2026-07-17T08:00:00Z",
                  "updated_at": "2026-07-17T08:20:00Z",
                  "seq_number": null,
                  "issued_at": null,
                  "line_items": [
                    {
                      "object": "receipt_line_item",
                      "id": "7c0f6c4e-9b1a-4c2e-9a5d-2f3b1e8a4d10",
                      "description": "Olio motore 5W30",
                      "quantity": 4,
                      "unit_type": "pz",
                      "unit_price": 18.5,
                      "vat_code": "22",
                      "tax_rate": 22,
                      "total_price": 74.0
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/MalformedRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      },
      "delete": {
        "tags": [
          "Receipts"
        ],
        "summary": "Delete a receipt (issued ones must be voided from the web first)",
        "x-summary-it": "Elimina uno scontrino",
        "x-scope": "receipts.write",
        "x-description-it": "Eliminazione logica, possibile finché lo scontrino è in bozza. Uno\nscontrino già emesso va prima annullato dal gestionale web:\nl'annullamento non è esposto su questa API.\n",
        "responses": {
          "200": {
            "description": "The removed receipt.",
            "x-description-it": "Lo scontrino eliminato.",
            "content": {
              "application/json": {
                "example": {
                  "object": "receipt",
                  "id": "3d7f1b95-6c28-4a04-9e51-8b2f7a3c6d19",
                  "type": "draft",
                  "status": "sale",
                  "title": "Tagliando 60.000 km",
                  "customer_id": "4c911f08-7713-4e83-bdf6-a0692a1b43fd",
                  "customer_name": "Mario Rossi",
                  "vehicle_id": "8821f0ac-5d2b-41e7-9a10-6c3f2b7d4e55",
                  "vehicle_name": "Fiat Panda",
                  "vehicle_license": "AB123CD",
                  "worksheet_id": "a5d21e64-8f37-4b90-ae12-3c7d6b048f95",
                  "sezionale_id": "c81f4a26-7d09-4e35-b6a1-2f8c5d3e9047",
                  "sezionale_code": "B",
                  "payment_method": "MP01",
                  "total_before_tax": 233.9,
                  "total_tax": 51.46,
                  "total_after_tax": 285.36,
                  "total_payments": 285.36,
                  "outstanding_payments": 0,
                  "electronic_receipts_enabled": false,
                  "e_receipt_status": null,
                  "voided_at": null,
                  "created_at": "2026-07-17T08:00:00Z",
                  "updated_at": "2026-07-17T08:20:00Z",
                  "seq_number": null,
                  "issued_at": null,
                  "deleted_at": "2026-07-20T11:04:00Z"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/receipts/{id}/issue": {
      "parameters": [
        {
          "$ref": "#/components/parameters/APIVersion"
        },
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        }
      ],
      "post": {
        "tags": [
          "Receipts"
        ],
        "summary": "Issue (paper numbering; 409 with electronic receipts enabled)",
        "x-summary-it": "Emetti uno scontrino",
        "x-scope": "receipts.write",
        "x-description-it": "Assegna il numero progressivo e valorizza `issued_at`.\n\nVale solo per la numerazione cartacea: se l'officina ha i\ncorrispettivi elettronici attivi (`electronic_receipts_enabled`) la\nrichiesta risponde `409`, perché in quel caso l'emissione passa dal\nregistratore telematico e non da questa API.\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "payment_method"
                ],
                "properties": {
                  "amount": {
                    "type": [
                      "number",
                      "null"
                    ],
                    "description": "Se omesso vale il residuo da incassare."
                  },
                  "payment_method": {
                    "type": "string"
                  },
                  "payment_date": {
                    "type": "string",
                    "description": "Formato `YYYY-MM-DD`, fra 1900-01-01 e 2100-12-31."
                  },
                  "financial_account_id": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "amount": 150.0,
                "payment_method": "MP01",
                "payment_date": "2026-07-16"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Issued receipt.",
            "x-description-it": "Lo scontrino emesso, con numero e data.",
            "content": {
              "application/json": {
                "example": {
                  "object": "receipt",
                  "id": "3d7f1b95-6c28-4a04-9e51-8b2f7a3c6d19",
                  "type": "issued",
                  "status": "sale",
                  "seq_number": 58,
                  "title": "Tagliando 60.000 km",
                  "customer_id": "4c911f08-7713-4e83-bdf6-a0692a1b43fd",
                  "customer_name": "Mario Rossi",
                  "vehicle_id": "8821f0ac-5d2b-41e7-9a10-6c3f2b7d4e55",
                  "vehicle_name": "Fiat Panda",
                  "vehicle_license": "AB123CD",
                  "worksheet_id": "a5d21e64-8f37-4b90-ae12-3c7d6b048f95",
                  "sezionale_id": "c81f4a26-7d09-4e35-b6a1-2f8c5d3e9047",
                  "sezionale_code": "B",
                  "payment_method": "MP01",
                  "total_before_tax": 233.9,
                  "total_tax": 51.46,
                  "total_after_tax": 285.36,
                  "total_payments": 285.36,
                  "outstanding_payments": 0,
                  "electronic_receipts_enabled": false,
                  "e_receipt_status": null,
                  "issued_at": "2026-07-17T08:20:00Z",
                  "voided_at": null,
                  "created_at": "2026-07-17T08:00:00Z",
                  "updated_at": "2026-07-17T08:20:00Z",
                  "line_items": [
                    {
                      "object": "receipt_line_item",
                      "id": "7c0f6c4e-9b1a-4c2e-9a5d-2f3b1e8a4d10",
                      "description": "Olio motore 5W30",
                      "quantity": 4,
                      "unit_type": "pz",
                      "unit_price": 18.5,
                      "vat_code": "22",
                      "tax_rate": 22,
                      "total_price": 74.0
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/receipts/{id}/payments": {
      "parameters": [
        {
          "$ref": "#/components/parameters/APIVersion"
        },
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        }
      ],
      "get": {
        "tags": [
          "Receipts"
        ],
        "summary": "List a receipt's payments",
        "x-summary-it": "Elenca i pagamenti dello scontrino",
        "x-scope": "receipts.read",
        "x-description-it": "Gli incassi registrati sullo scontrino. Il residuo è\n`outstanding_payments` sullo scontrino stesso.\n",
        "responses": {
          "200": {
            "description": "Payments, oldest first (unpaginated).",
            "x-description-it": "I pagamenti dello scontrino.",
            "content": {
              "application/json": {
                "example": {
                  "object": "list",
                  "data": [
                    {
                      "object": "payment",
                      "id": "d47c0b58-3e91-42a6-8f05-1b9d6a3e2c74",
                      "amount": 285.36,
                      "payment_method": "MP01",
                      "payment_date": "2026-07-17",
                      "created_at": "2026-07-17T08:20:00Z"
                    }
                  ],
                  "has_more": false,
                  "url": "/v1/receipts/3d7f1b95-6c28-4a04-9e51-8b2f7a3c6d19/payments"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      },
      "post": {
        "tags": [
          "Receipts"
        ],
        "summary": "Register a payment (PaymentCreateRequest; method falls back to the receipt's)",
        "x-summary-it": "Registra un pagamento",
        "x-scope": "receipts.write",
        "x-description-it": "Registra un incasso sullo scontrino. `payment_method` usa i codici\nministeriali (`MP01` contanti, `MP05` bonifico, `MP08` carta); se\nomesso vale quello dello scontrino. L'importo può essere parziale.\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "payment_method"
                ],
                "properties": {
                  "amount": {
                    "type": [
                      "number",
                      "null"
                    ],
                    "description": "Se omesso vale il residuo da incassare."
                  },
                  "payment_method": {
                    "type": "string"
                  },
                  "payment_date": {
                    "type": "string",
                    "description": "Formato `YYYY-MM-DD`, fra 1900-01-01 e 2100-12-31."
                  },
                  "financial_account_id": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "amount": 150.0,
                "payment_method": "MP01",
                "payment_date": "2026-07-16"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Payment.",
            "x-description-it": "Il pagamento registrato.",
            "content": {
              "application/json": {
                "example": {
                  "object": "payment",
                  "id": "d47c0b58-3e91-42a6-8f05-1b9d6a3e2c74",
                  "amount": 285.36,
                  "payment_method": "MP01",
                  "payment_date": "2026-07-17",
                  "created_at": "2026-07-17T08:20:00Z"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/MalformedRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/receipts/{id}/payments/{paymentID}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/APIVersion"
        },
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        },
        {
          "name": "paymentID",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        }
      ],
      "delete": {
        "tags": [
          "Receipts"
        ],
        "summary": "Remove a payment from a receipt",
        "x-summary-it": "Elimina un pagamento",
        "x-scope": "receipts.write",
        "x-description-it": "Rimuove l'incasso e riporta il residuo dello scontrino al valore\nprecedente.\n",
        "responses": {
          "200": {
            "description": "The removed payment.",
            "x-description-it": "Il pagamento eliminato.",
            "content": {
              "application/json": {
                "example": {
                  "object": "payment",
                  "id": "d47c0b58-3e91-42a6-8f05-1b9d6a3e2c74",
                  "amount": 285.36,
                  "payment_method": "MP01",
                  "payment_date": "2026-07-17",
                  "created_at": "2026-07-17T08:20:00Z"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/report/{widget}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/APIVersion"
        },
        {
          "name": "widget",
          "in": "path",
          "required": true,
          "description": "Widget key. One route per data widget so clients fetch widgets\nindependently (progressive loading) and new widgets are purely\nadditive. Current keys: `kpis`, `sales-chart`,\n`expenses-chart`, `quote-conversion`, `worksheet-status`,\n`outstanding-worksheets`, `upcoming-appointments`,\n`top-customers`, `top-services`, `worksheet-margin-chart`,\n`worksheet-margins`. Unknown keys → 404.\n",
          "schema": {
            "type": "string"
          }
        }
      ],
      "get": {
        "tags": [
          "Reports"
        ],
        "summary": "One data widget of the Situazione report",
        "x-summary-it": "Recupera un widget di report",
        "x-scope": "reports.read",
        "x-description-it": "Restituisce i dati di un singolo widget delle statistiche officina.\nIl nome del widget va nel percorso; l'intervallo si sceglie con\n`preset` oppure con `from` / `to`.\n\nLa risposta è una busta uniforme: `widget` ripete il widget\nrichiesto, `preset`, `from` e `to` riportano l'intervallo\neffettivamente applicato e `data` contiene il risultato, la cui forma\ndipende dal widget.\n",
        "description": "Returns the data behind one widget of the mobile Situazione\nscreen, wrapped in a `report_widget` envelope that echoes the\napplied period. Backed by the same report services as the web\nreport page; requires the `reports.read` permission (403\notherwise).\n\nThe period uses the same date filter as the list endpoints: no\ndate params means the last 12 months, and an explicit\n`preset`/`from`/`to` applies to that request only.\n`preset: all` spans from the company's creation to today.\n\nChart widgets bucket by **day** for periods up to 31 days and by\n**month** otherwise — `x_axis` entries are always `YYYY-MM-DD`,\nso clients re-bucket freely (the mobile app aggregates daily\naxes into weeks).\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/DatePreset"
          },
          {
            "name": "from",
            "in": "query",
            "required": false,
            "description": "Period start (default: 12 months ago).",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "to",
            "in": "query",
            "required": false,
            "description": "Period end, inclusive (default: today).",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "List widgets only — rows returned. Default 5, max 50 (the\nrepository cap; raise both to go beyond).\n",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 50
            }
          },
          {
            "name": "metric",
            "in": "query",
            "required": false,
            "description": "`worksheet-margins` only — ranking metric.",
            "schema": {
              "type": "string",
              "enum": [
                "eur",
                "pct"
              ]
            }
          },
          {
            "name": "direction",
            "in": "query",
            "required": false,
            "description": "`worksheet-margins` only.",
            "schema": {
              "type": "string",
              "enum": [
                "best",
                "worst"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Widget data.",
            "x-description-it": "I dati del widget richiesto.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ReportWidgetEnvelope"
                },
                "example": {
                  "object": "report_widget",
                  "widget": "revenue_by_month",
                  "preset": "year",
                  "from": "2026-01-01",
                  "to": "2026-12-31",
                  "data": [
                    {
                      "label": "2026-06",
                      "value": 12480.5
                    },
                    {
                      "label": "2026-07",
                      "value": 15230.0
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/sezionali": {
      "parameters": [
        {
          "$ref": "#/components/parameters/APIVersion"
        }
      ],
      "get": {
        "tags": [
          "Invoices"
        ],
        "summary": "List invoice sezionali",
        "x-summary-it": "Elenca i sezionali",
        "x-scope": "settings.read",
        "x-description-it": "I registri di numerazione configurati dall'officina, da usare per\nvalorizzare `sezionale_id`. È una voce di Impostazioni, quindi\nrichiede il permesso `settings.read`.\n",
        "description": "The company's invoice numbering registers, behind the fattura\nedit form's Sezionale select. Unpaginated — the set is a\nhandful of rows. `has_more` is always `false`.\n",
        "responses": {
          "200": {
            "description": "Sezionali.",
            "x-description-it": "I sezionali configurati.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "object",
                    "data",
                    "has_more",
                    "url"
                  ],
                  "properties": {
                    "object": {
                      "type": "string",
                      "const": "list"
                    },
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Sezionale"
                      }
                    },
                    "has_more": {
                      "type": "boolean",
                      "const": false
                    },
                    "url": {
                      "type": "string"
                    }
                  }
                },
                "example": {
                  "object": "list",
                  "data": [
                    {
                      "object": "sezionale",
                      "id": "c81f4a26-7d09-4e35-b6a1-2f8c5d3e9047",
                      "code": "A",
                      "label": "Sezionale principale"
                    },
                    {
                      "object": "sezionale",
                      "id": "5a9c2e71-4b83-40df-9126-7e3d1f6a8c25",
                      "code": "B",
                      "label": "Corrispettivi"
                    }
                  ],
                  "has_more": false,
                  "url": "/v1/sezionali"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      },
      "post": {
        "tags": [
          "Settings"
        ],
        "summary": "Create a sezionale",
        "x-summary-it": "Crea un sezionale",
        "x-scope": "settings.write",
        "x-description-it": "Crea un registro di numerazione. `code` è il suffisso che compare sul\nnumero del documento.\n",
        "operationId": "sezionaleCreate",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "code",
                  "label"
                ],
                "properties": {
                  "code": {
                    "type": "string"
                  },
                  "label": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "code": "A",
                "label": "Sezionale principale"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "success",
            "x-description-it": "Il sezionale creato.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/json": {
                "example": {
                  "object": "sezionale",
                  "id": "c81f4a26-7d09-4e35-b6a1-2f8c5d3e9047",
                  "code": "A",
                  "label": "Sezionale principale"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/sezionali/{id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/APIVersion"
        },
        {
          "name": "id",
          "in": "path",
          "required": true,
          "description": "Identifier of the target resource.",
          "schema": {
            "type": "string"
          }
        }
      ],
      "delete": {
        "tags": [
          "Settings"
        ],
        "summary": "Delete sezionale",
        "x-summary-it": "Elimina un sezionale",
        "x-scope": "settings.write",
        "x-description-it": "Elimina il sezionale. Non è eliminabile se è già stato usato per\nnumerare un documento.\n",
        "operationId": "sezionaleDelete",
        "responses": {
          "200": {
            "description": "success",
            "x-description-it": "Il sezionale eliminato.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/json": {
                "example": {
                  "object": "sezionale",
                  "id": "c81f4a26-7d09-4e35-b6a1-2f8c5d3e9047"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      },
      "patch": {
        "tags": [
          "Settings"
        ],
        "summary": "Update sezionale",
        "x-summary-it": "Aggiorna un sezionale",
        "x-scope": "settings.write",
        "x-description-it": "Aggiorna codice ed etichetta del sezionale.\n",
        "operationId": "sezionalePatch",
        "requestBody": {
          "required": true,
          "description": "Il corpo è lo stesso della creazione: va inviato\nl'oggetto completo, non solo i campi da cambiare.\n",
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "code",
                  "label"
                ],
                "properties": {
                  "code": {
                    "type": "string"
                  },
                  "label": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "code": "A",
                "label": "Sezionale principale"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "success",
            "x-description-it": "Il sezionale aggiornato.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/json": {
                "example": {
                  "object": "sezionale",
                  "id": "c81f4a26-7d09-4e35-b6a1-2f8c5d3e9047",
                  "code": "A",
                  "label": "Sezionale principale"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/suppliers": {
      "parameters": [
        {
          "$ref": "#/components/parameters/APIVersion"
        }
      ],
      "get": {
        "tags": [
          "Suppliers"
        ],
        "summary": "List suppliers (?q= name substring, cursor pagination)",
        "x-summary-it": "Elenca i fornitori",
        "x-scope": "suppliers.read",
        "x-description-it": "I fornitori dell'officina. Filtra per testo con `q`.\n",
        "parameters": [
          {
            "name": "direction",
            "in": "query",
            "required": false,
            "description": "Direzione di ordinamento: `asc` o `desc`.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Numero di risultati per pagina.",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "q",
            "in": "query",
            "required": false,
            "description": "Ricerca testuale libera.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "sort",
            "in": "query",
            "required": false,
            "description": "Colonna di ordinamento.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "starting_after",
            "in": "query",
            "required": false,
            "description": "Cursore opaco della pagina precedente. Va restituito invariato.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Suppliers.",
            "x-description-it": "I fornitori dell'officina.",
            "content": {
              "application/json": {
                "example": {
                  "object": "list",
                  "data": [
                    {
                      "object": "supplier",
                      "id": "6f2b8c14-9d70-4a35-b8e2-1c5f7a3d9e60",
                      "name": "Ricambi Lombardia S.r.l.",
                      "email": "ordini@ricambi.example.it",
                      "phone": "+390212345678",
                      "phone_national": "02 1234 5678",
                      "address": "Via Mecenate 12",
                      "city": "Milano",
                      "zip_code": "20138",
                      "tax_id": "01234567890",
                      "comments": null,
                      "bank_account_iban": "IT60X0542811101000000123456",
                      "website": "https://ricambi.example.it",
                      "website_display": "ricambi.example.it",
                      "supplier_type": "ricambi",
                      "created_at": "2025-02-11T10:00:00Z",
                      "updated_at": "2026-06-03T14:20:00Z"
                    }
                  ],
                  "has_more": false,
                  "url": "/v1/suppliers"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      },
      "post": {
        "tags": [
          "Suppliers"
        ],
        "summary": "Create a supplier",
        "x-summary-it": "Crea un fornitore",
        "x-scope": "suppliers.write",
        "x-description-it": "Crea un fornitore. Il telefono va in formato E.164 (`+390212345678`)\ne il sito con lo schema completo (`https://...`).\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "name"
                ],
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "email": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "phone": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "description": "In formato E.164, es. `+390212345678`."
                  },
                  "address": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "city": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "zip_code": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "tax_id": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "comments": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "bank_account_iban": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "website": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "description": "URL completo, es. `https://ricambi.example.it`."
                  }
                }
              },
              "example": {
                "name": "Ricambi Lombardia S.r.l.",
                "email": "ordini@ricambi.example.it",
                "phone": "+390212345678",
                "city": "Milano",
                "zip_code": "20100",
                "tax_id": "01234567890",
                "website": "https://ricambi.example.it"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Supplier.",
            "x-description-it": "Il fornitore creato.",
            "content": {
              "application/json": {
                "example": {
                  "object": "supplier",
                  "id": "6f2b8c14-9d70-4a35-b8e2-1c5f7a3d9e60",
                  "name": "Ricambi Lombardia S.r.l.",
                  "email": "ordini@ricambi.example.it",
                  "phone": "+390212345678",
                  "phone_national": "02 1234 5678",
                  "address": "Via Mecenate 12",
                  "city": "Milano",
                  "zip_code": "20138",
                  "tax_id": "01234567890",
                  "comments": null,
                  "bank_account_iban": "IT60X0542811101000000123456",
                  "website": "https://ricambi.example.it",
                  "website_display": "ricambi.example.it",
                  "supplier_type": "ricambi",
                  "created_at": "2025-02-11T10:00:00Z",
                  "updated_at": "2026-06-03T14:20:00Z"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/MalformedRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/suppliers/{id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/APIVersion"
        },
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        }
      ],
      "get": {
        "tags": [
          "Suppliers"
        ],
        "summary": "Retrieve a supplier",
        "x-summary-it": "Recupera un fornitore",
        "x-scope": "suppliers.read",
        "x-description-it": "Restituisce il fornitore con i riepiloghi collegati: ordini,\narticoli forniti e spese.\n",
        "responses": {
          "200": {
            "description": "Supplier.",
            "x-description-it": "Il fornitore richiesto.",
            "content": {
              "application/json": {
                "example": {
                  "object": "supplier",
                  "id": "6f2b8c14-9d70-4a35-b8e2-1c5f7a3d9e60",
                  "name": "Ricambi Lombardia S.r.l.",
                  "email": "ordini@ricambi.example.it",
                  "phone": "+390212345678",
                  "phone_national": "02 1234 5678",
                  "address": "Via Mecenate 12",
                  "city": "Milano",
                  "zip_code": "20138",
                  "tax_id": "01234567890",
                  "comments": null,
                  "bank_account_iban": "IT60X0542811101000000123456",
                  "website": "https://ricambi.example.it",
                  "website_display": "ricambi.example.it",
                  "supplier_type": "ricambi",
                  "created_at": "2025-02-11T10:00:00Z",
                  "updated_at": "2026-06-03T14:20:00Z"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      },
      "patch": {
        "tags": [
          "Suppliers"
        ],
        "summary": "Update a supplier",
        "x-summary-it": "Aggiorna un fornitore",
        "x-scope": "suppliers.write",
        "x-description-it": "Aggiorna solo i campi presenti nel corpo: gli altri restano\ninvariati.\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "email": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "phone": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "description": "In formato E.164, es. `+390212345678`."
                  },
                  "address": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "city": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "zip_code": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "tax_id": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "comments": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "bank_account_iban": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "website": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "description": "URL completo, es. `https://ricambi.example.it`."
                  }
                }
              },
              "example": {
                "name": "Ricambi Lombardia S.r.l.",
                "email": "ordini@ricambi.example.it",
                "phone": "+390212345678",
                "city": "Milano",
                "zip_code": "20100",
                "tax_id": "01234567890",
                "website": "https://ricambi.example.it"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated supplier.",
            "x-description-it": "Il fornitore aggiornato.",
            "content": {
              "application/json": {
                "example": {
                  "object": "supplier",
                  "id": "6f2b8c14-9d70-4a35-b8e2-1c5f7a3d9e60",
                  "name": "Ricambi Lombardia S.r.l.",
                  "email": "ordini@ricambi.example.it",
                  "phone": "+390212345678",
                  "phone_national": "02 1234 5678",
                  "address": "Via Mecenate 12",
                  "city": "Milano",
                  "zip_code": "20138",
                  "tax_id": "01234567890",
                  "comments": null,
                  "bank_account_iban": "IT60X0542811101000000123456",
                  "website": "https://ricambi.example.it",
                  "website_display": "ricambi.example.it",
                  "supplier_type": "ricambi",
                  "created_at": "2025-02-11T10:00:00Z",
                  "updated_at": "2026-06-03T14:20:00Z"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/MalformedRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      },
      "delete": {
        "tags": [
          "Suppliers"
        ],
        "summary": "Delete a supplier",
        "x-summary-it": "Elimina un fornitore",
        "x-scope": "suppliers.write",
        "x-description-it": "Eliminazione logica del fornitore. Ordini e spese già registrati\nrestano intatti.\n",
        "responses": {
          "200": {
            "description": "The removed supplier.",
            "x-description-it": "Il fornitore eliminato.",
            "content": {
              "application/json": {
                "example": {
                  "object": "supplier",
                  "id": "6f2b8c14-9d70-4a35-b8e2-1c5f7a3d9e60",
                  "name": "Ricambi Lombardia S.r.l.",
                  "email": "ordini@ricambi.example.it",
                  "phone": "+390212345678",
                  "phone_national": "02 1234 5678",
                  "address": "Via Mecenate 12",
                  "city": "Milano",
                  "zip_code": "20138",
                  "tax_id": "01234567890",
                  "comments": null,
                  "bank_account_iban": "IT60X0542811101000000123456",
                  "website": "https://ricambi.example.it",
                  "website_display": "ricambi.example.it",
                  "supplier_type": "ricambi",
                  "created_at": "2025-02-11T10:00:00Z",
                  "updated_at": "2026-06-03T14:20:00Z"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/team": {
      "parameters": [
        {
          "$ref": "#/components/parameters/APIVersion"
        }
      ],
      "get": {
        "tags": [
          "Settings"
        ],
        "summary": "List teams",
        "x-summary-it": "Elenca i collaboratori",
        "x-scope": "settings.read",
        "x-description-it": "I collaboratori dell'officina, con ruolo e stato di attivazione.\n",
        "operationId": "teamList",
        "description": "(read-only; invites and permissions stay web-side).\n",
        "responses": {
          "200": {
            "description": "success",
            "x-description-it": "I collaboratori dell'officina.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/json": {
                "example": {
                  "object": "list",
                  "data": [
                    {
                      "object": "team_member",
                      "id": "4e7b1d29-5a63-40c8-9f15-2b8d6e3a1c74",
                      "first_name": "Luca",
                      "last_name": "Bianchi",
                      "email": "luca.bianchi@example.it",
                      "phone": "+393337654321",
                      "role": "meccanico",
                      "deactivated": false,
                      "is_self": false
                    }
                  ],
                  "has_more": false,
                  "url": "/v1/team"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/team/{id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/APIVersion"
        },
        {
          "name": "id",
          "in": "path",
          "required": true,
          "description": "Identifier of the target resource.",
          "schema": {
            "type": "string"
          }
        }
      ],
      "get": {
        "tags": [
          "Settings"
        ],
        "summary": "Retrieve a team",
        "x-summary-it": "Recupera un collaboratore",
        "x-scope": "settings.read",
        "x-description-it": "Restituisce il collaboratore con i gruppi di permessi assegnati.\n",
        "operationId": "teamGet",
        "description": "(read-only): identity, stato, ruolo and the functional-area permissions.\nDeactivated members are included so their card still opens.\n",
        "responses": {
          "200": {
            "description": "success",
            "x-description-it": "Il collaboratore richiesto.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/json": {
                "example": {
                  "object": "team_member",
                  "id": "4e7b1d29-5a63-40c8-9f15-2b8d6e3a1c74",
                  "first_name": "Luca",
                  "last_name": "Bianchi",
                  "email": "luca.bianchi@example.it",
                  "phone": "+393337654321",
                  "role": "meccanico",
                  "deactivated": false,
                  "is_self": false,
                  "permission_groups": [
                    {
                      "name": "Officina",
                      "granted": true
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/tyresets": {
      "parameters": [
        {
          "$ref": "#/components/parameters/APIVersion"
        }
      ],
      "get": {
        "tags": [
          "TyreSets"
        ],
        "summary": "List tyre sets",
        "x-summary-it": "Elenca i treni in deposito",
        "x-scope": "articles.read",
        "x-description-it": "I treni di gomme custoditi per i clienti. Filtra per stato con\n`status` e per testo con `q`.\n\nIl deposito gomme rientra nel permesso `articles`, non ne ha uno\nproprio: una chiave con `articles.read` vede sia il magazzino sia il\ndeposito.\n",
        "operationId": "tyreSetList",
        "description": "?status, ?customer_id, cursor pagination).\n",
        "parameters": [
          {
            "name": "customer_id",
            "in": "query",
            "required": false,
            "description": "Filtra per cliente.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Numero di risultati per pagina.",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "location",
            "in": "query",
            "required": false,
            "description": "Filtra per posizione di deposito.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "q",
            "in": "query",
            "required": false,
            "description": "Ricerca testuale libera.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "rim_type",
            "in": "query",
            "required": false,
            "description": "Filtra per tipo di cerchio.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "starting_after",
            "in": "query",
            "required": false,
            "description": "Cursore opaco della pagina precedente. Va restituito invariato.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "description": "Filtra per stato.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "success",
            "x-description-it": "I treni in deposito.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/json": {
                "example": {
                  "object": "list",
                  "data": [
                    {
                      "object": "tyre_set",
                      "id": "47e2c9b1-8d50-4f36-a127-6c3b9e5d2a84",
                      "seq_number": 63,
                      "status": "deposited",
                      "description": "Michelin Primacy 195/65 R15",
                      "quantity": 4,
                      "rim_type": "lega",
                      "season_type": "estive",
                      "condition": "buono",
                      "residual_mm": 6,
                      "location": "Scaffale B3",
                      "comments": null,
                      "customer_id": "4c911f08-7713-4e83-bdf6-a0692a1b43fd",
                      "customer_name": "Mario Rossi",
                      "vehicle_id": "8821f0ac-5d2b-41e7-9a10-6c3f2b7d4e55",
                      "vehicle_name": "Fiat Panda",
                      "vehicle_license": "AB123CD",
                      "deposited_at": "2026-04-10T09:00:00Z",
                      "mounted_at": null,
                      "collected_at": null,
                      "disposed_at": null,
                      "inventory_check_at": null,
                      "created_at": "2026-04-10T09:00:00Z",
                      "updated_at": "2026-04-10T09:00:00Z"
                    }
                  ],
                  "has_more": false,
                  "url": "/v1/tyresets"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      },
      "post": {
        "tags": [
          "TyreSets"
        ],
        "summary": "Create a tyre set",
        "x-summary-it": "Registra un treno in deposito",
        "x-scope": "articles.write",
        "x-description-it": "Prende in custodia un treno di gomme per un cliente. Servono\n`customer_id`, `vehicle_id`, `description` e `rim_type`.\n",
        "operationId": "tyreSetCreate",
        "description": "customer's vehicle (status starts deposited, web parity).\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "customer_id",
                  "vehicle_id",
                  "description",
                  "rim_type"
                ],
                "properties": {
                  "customer_id": {
                    "type": "string"
                  },
                  "vehicle_id": {
                    "type": "string"
                  },
                  "description": {
                    "type": "string"
                  },
                  "quantity": {
                    "type": [
                      "integer",
                      "null"
                    ]
                  },
                  "rim_type": {
                    "type": "string"
                  },
                  "season_type": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "condition": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "residual_mm": {
                    "type": [
                      "integer",
                      "null"
                    ]
                  },
                  "location": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "comments": {
                    "type": [
                      "string",
                      "null"
                    ]
                  }
                }
              },
              "example": {
                "customer_id": "4c911f08-7713-4e83-bdf6-a0692a1b43fd",
                "vehicle_id": "8821f0ac-5d2b-41e7-9a10-6c3f2b7d4e55",
                "description": "Michelin Primacy 195/65 R15",
                "quantity": 4,
                "rim_type": "lega",
                "season_type": "estive",
                "residual_mm": 6,
                "location": "Scaffale B3"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "success",
            "x-description-it": "Il treno registrato.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/json": {
                "example": {
                  "object": "tyre_set",
                  "id": "47e2c9b1-8d50-4f36-a127-6c3b9e5d2a84",
                  "seq_number": 63,
                  "status": "deposited",
                  "description": "Michelin Primacy 195/65 R15",
                  "quantity": 4,
                  "rim_type": "lega",
                  "season_type": "estive",
                  "condition": "buono",
                  "residual_mm": 6,
                  "location": "Scaffale B3",
                  "comments": null,
                  "customer_id": "4c911f08-7713-4e83-bdf6-a0692a1b43fd",
                  "customer_name": "Mario Rossi",
                  "vehicle_id": "8821f0ac-5d2b-41e7-9a10-6c3f2b7d4e55",
                  "vehicle_name": "Fiat Panda",
                  "vehicle_license": "AB123CD",
                  "deposited_at": "2026-04-10T09:00:00Z",
                  "mounted_at": null,
                  "collected_at": null,
                  "disposed_at": null,
                  "inventory_check_at": null,
                  "created_at": "2026-04-10T09:00:00Z",
                  "updated_at": "2026-04-10T09:00:00Z"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/MalformedRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/tyresets/status-counts": {
      "parameters": [
        {
          "$ref": "#/components/parameters/APIVersion"
        }
      ],
      "get": {
        "tags": [
          "TyreSets"
        ],
        "summary": "Replace tyre status counts",
        "x-summary-it": "Conta i treni per stato",
        "x-scope": "articles.read",
        "x-description-it": "Il numero di treni in ciascuno stato, con gli stessi filtri\ndell'elenco.\n",
        "operationId": "tyreSetStatusCounts",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": false,
            "description": "Ricerca testuale libera.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "success",
            "x-description-it": "Il numero di treni per stato.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/json": {
                "example": {
                  "object": "tyre_set_status_counts",
                  "deposited": 18,
                  "mounted": 4,
                  "collected": 61,
                  "disposed": 3,
                  "locations": [
                    "Scaffale B3",
                    "Magazzino esterno"
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/tyresets/{id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/APIVersion"
        },
        {
          "name": "id",
          "in": "path",
          "required": true,
          "description": "Identifier of the target resource.",
          "schema": {
            "type": "string"
          }
        }
      ],
      "delete": {
        "tags": [
          "TyreSets"
        ],
        "summary": "Delete tyre set",
        "x-summary-it": "Elimina un treno dal deposito",
        "x-scope": "articles.write",
        "x-description-it": "Eliminazione logica del treno in deposito.\n",
        "operationId": "tyreSetDelete",
        "responses": {
          "200": {
            "description": "success",
            "x-description-it": "Il treno eliminato.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/json": {
                "example": {
                  "object": "tyre_set",
                  "id": "47e2c9b1-8d50-4f36-a127-6c3b9e5d2a84",
                  "seq_number": 63,
                  "status": "deposited",
                  "description": "Michelin Primacy 195/65 R15",
                  "quantity": 4,
                  "rim_type": "lega",
                  "season_type": "estive",
                  "condition": "buono",
                  "residual_mm": 6,
                  "location": "Scaffale B3",
                  "comments": null,
                  "customer_id": "4c911f08-7713-4e83-bdf6-a0692a1b43fd",
                  "customer_name": "Mario Rossi",
                  "vehicle_id": "8821f0ac-5d2b-41e7-9a10-6c3f2b7d4e55",
                  "vehicle_name": "Fiat Panda",
                  "vehicle_license": "AB123CD",
                  "deposited_at": "2026-04-10T09:00:00Z",
                  "mounted_at": null,
                  "collected_at": null,
                  "disposed_at": null,
                  "inventory_check_at": null,
                  "created_at": "2026-04-10T09:00:00Z",
                  "updated_at": "2026-04-10T09:00:00Z"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      },
      "get": {
        "tags": [
          "TyreSets"
        ],
        "summary": "Retrieve a tyre set",
        "x-summary-it": "Recupera un treno in deposito",
        "x-scope": "articles.read",
        "x-description-it": "Restituisce il treno con le date dei passaggi di stato:\n`deposited_at`, `mounted_at`, `collected_at`, `disposed_at`.\n",
        "operationId": "tyreSetGet",
        "responses": {
          "200": {
            "description": "success",
            "x-description-it": "Il treno richiesto.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/json": {
                "example": {
                  "object": "tyre_set",
                  "id": "47e2c9b1-8d50-4f36-a127-6c3b9e5d2a84",
                  "seq_number": 63,
                  "status": "deposited",
                  "description": "Michelin Primacy 195/65 R15",
                  "quantity": 4,
                  "rim_type": "lega",
                  "season_type": "estive",
                  "condition": "buono",
                  "residual_mm": 6,
                  "location": "Scaffale B3",
                  "comments": null,
                  "customer_id": "4c911f08-7713-4e83-bdf6-a0692a1b43fd",
                  "customer_name": "Mario Rossi",
                  "vehicle_id": "8821f0ac-5d2b-41e7-9a10-6c3f2b7d4e55",
                  "vehicle_name": "Fiat Panda",
                  "vehicle_license": "AB123CD",
                  "deposited_at": "2026-04-10T09:00:00Z",
                  "mounted_at": null,
                  "collected_at": null,
                  "disposed_at": null,
                  "inventory_check_at": null,
                  "created_at": "2026-04-10T09:00:00Z",
                  "updated_at": "2026-04-10T09:00:00Z"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      },
      "patch": {
        "tags": [
          "TyreSets"
        ],
        "summary": "Update tyre set",
        "x-summary-it": "Aggiorna un treno in deposito",
        "x-scope": "articles.write",
        "x-description-it": "Il corpo è lo stesso della creazione: va inviato l'oggetto completo,\nnon solo i campi da cambiare.\n",
        "operationId": "tyreSetPatch",
        "description": "the descriptive fields (status moves via transitions).\n",
        "requestBody": {
          "required": true,
          "description": "Il corpo è lo stesso della creazione: va inviato\nl'oggetto completo, non solo i campi da cambiare.\n",
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "customer_id",
                  "vehicle_id",
                  "description",
                  "rim_type"
                ],
                "properties": {
                  "customer_id": {
                    "type": "string"
                  },
                  "vehicle_id": {
                    "type": "string"
                  },
                  "description": {
                    "type": "string"
                  },
                  "quantity": {
                    "type": [
                      "integer",
                      "null"
                    ]
                  },
                  "rim_type": {
                    "type": "string"
                  },
                  "season_type": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "condition": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "residual_mm": {
                    "type": [
                      "integer",
                      "null"
                    ]
                  },
                  "location": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "comments": {
                    "type": [
                      "string",
                      "null"
                    ]
                  }
                }
              },
              "example": {
                "customer_id": "4c911f08-7713-4e83-bdf6-a0692a1b43fd",
                "vehicle_id": "8821f0ac-5d2b-41e7-9a10-6c3f2b7d4e55",
                "description": "Michelin Primacy 195/65 R15",
                "quantity": 4,
                "rim_type": "lega",
                "season_type": "estive",
                "residual_mm": 6,
                "location": "Scaffale B3"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "success",
            "x-description-it": "Il treno aggiornato.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/json": {
                "example": {
                  "object": "tyre_set",
                  "id": "47e2c9b1-8d50-4f36-a127-6c3b9e5d2a84",
                  "seq_number": 63,
                  "status": "deposited",
                  "description": "Michelin Primacy 195/65 R15",
                  "quantity": 4,
                  "rim_type": "lega",
                  "season_type": "estive",
                  "condition": "buono",
                  "residual_mm": 6,
                  "location": "Scaffale B3",
                  "comments": null,
                  "customer_id": "4c911f08-7713-4e83-bdf6-a0692a1b43fd",
                  "customer_name": "Mario Rossi",
                  "vehicle_id": "8821f0ac-5d2b-41e7-9a10-6c3f2b7d4e55",
                  "vehicle_name": "Fiat Panda",
                  "vehicle_license": "AB123CD",
                  "deposited_at": "2026-04-10T09:00:00Z",
                  "mounted_at": null,
                  "collected_at": null,
                  "disposed_at": null,
                  "inventory_check_at": null,
                  "created_at": "2026-04-10T09:00:00Z",
                  "updated_at": "2026-04-10T09:00:00Z"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/MalformedRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/tyresets/{id}/transitions": {
      "parameters": [
        {
          "$ref": "#/components/parameters/APIVersion"
        },
        {
          "name": "id",
          "in": "path",
          "required": true,
          "description": "Identifier of the target resource.",
          "schema": {
            "type": "string"
          }
        }
      ],
      "post": {
        "tags": [
          "TyreSets"
        ],
        "summary": "Change the status of tyre set",
        "x-summary-it": "Cambia stato al treno",
        "x-scope": "articles.write",
        "x-description-it": "Registra un passaggio di stato (montato, ritirato, smaltito) e\nvalorizza la data corrispondente. È l'unico modo di cambiare\n`status`: non impostarlo via PATCH, perché non aggiornerebbe le date.\n",
        "operationId": "tyreSetTransition",
        "description": "{action: deposited|mounted|disposed|collected}.\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "action"
                ],
                "properties": {
                  "action": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "action": "ritirato"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "success",
            "x-description-it": "Il treno dopo il passaggio di stato.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/json": {
                "example": {
                  "object": "tyre_set",
                  "id": "47e2c9b1-8d50-4f36-a127-6c3b9e5d2a84",
                  "seq_number": 63,
                  "status": "mounted",
                  "description": "Michelin Primacy 195/65 R15",
                  "quantity": 4,
                  "rim_type": "lega",
                  "season_type": "estive",
                  "condition": "buono",
                  "residual_mm": 6,
                  "location": "Scaffale B3",
                  "comments": null,
                  "customer_id": "4c911f08-7713-4e83-bdf6-a0692a1b43fd",
                  "customer_name": "Mario Rossi",
                  "vehicle_id": "8821f0ac-5d2b-41e7-9a10-6c3f2b7d4e55",
                  "vehicle_name": "Fiat Panda",
                  "vehicle_license": "AB123CD",
                  "deposited_at": "2026-04-10T09:00:00Z",
                  "mounted_at": "2026-08-04T09:00:00Z",
                  "collected_at": null,
                  "disposed_at": null,
                  "inventory_check_at": null,
                  "created_at": "2026-04-10T09:00:00Z",
                  "updated_at": "2026-04-10T09:00:00Z"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/MalformedRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/vat-rates": {
      "parameters": [
        {
          "$ref": "#/components/parameters/APIVersion"
        }
      ],
      "get": {
        "tags": [
          "Articles"
        ],
        "summary": "List company VAT rates",
        "x-summary-it": "Elenca le aliquote IVA",
        "x-scope": "settings.read",
        "x-description-it": "Le aliquote attive dell'azienda, da usare per valorizzare `vat_code`\nsugli articoli e sulle righe dei documenti. È una voce di\nImpostazioni, quindi richiede il permesso `settings.read`.\n",
        "description": "The VAT rates enabled for the authenticated user's company, in\nthe company's configured order — the first row is the default\nfor new line items. Codes that are disabled but still referenced\nby existing documents are accepted on write but not offered\nhere.\n\nIntentionally unpaginated: the set is a handful of codes.\n`has_more` is always `false`.\n",
        "responses": {
          "200": {
            "description": "Company-enabled VAT rates.",
            "x-description-it": "Le aliquote IVA attive.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "object",
                    "data",
                    "has_more",
                    "url"
                  ],
                  "properties": {
                    "object": {
                      "type": "string",
                      "const": "list"
                    },
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/VatRate"
                      }
                    },
                    "has_more": {
                      "type": "boolean",
                      "const": false
                    },
                    "url": {
                      "type": "string"
                    }
                  }
                },
                "example": {
                  "object": "list",
                  "data": [
                    {
                      "object": "vat_rate",
                      "code": "22",
                      "rate": 22,
                      "description": "Aliquota ordinaria"
                    },
                    {
                      "object": "vat_rate",
                      "code": "10",
                      "rate": 10,
                      "description": "Aliquota ridotta"
                    },
                    {
                      "object": "vat_rate",
                      "code": "N2.2",
                      "rate": 0,
                      "description": "Non soggetta"
                    }
                  ],
                  "has_more": false,
                  "url": "/v1/vat-rates"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      },
      "put": {
        "tags": [
          "Settings"
        ],
        "summary": "Replace vat rate",
        "x-summary-it": "Imposta le aliquote IVA attive",
        "x-scope": "settings.write",
        "x-description-it": "Sostituisce l'intero insieme delle aliquote attive: quelle non\nelencate vengono disattivate. I codici ammessi sono quelli di\n`GET /v1/vat-rates/catalog`.\n",
        "operationId": "vatRateSet",
        "description": "set with the given ordered codes.\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "codes"
                ],
                "properties": {
                  "codes": {
                    "type": "array",
                    "description": "L'insieme completo delle aliquote attive. Le aliquote non\nelencate vengono disattivate.\n",
                    "items": {
                      "type": "string"
                    }
                  }
                }
              },
              "example": {
                "codes": [
                  "22",
                  "10",
                  "4"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "success",
            "x-description-it": "Le aliquote ora attive.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/json": {
                "example": {
                  "object": "vat_rates",
                  "codes": [
                    "22",
                    "10"
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/MalformedRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/vat-rates/catalog": {
      "parameters": [
        {
          "$ref": "#/components/parameters/APIVersion"
        }
      ],
      "get": {
        "tags": [
          "Settings"
        ],
        "summary": "Vat rate catalog",
        "x-summary-it": "Catalogo delle aliquote IVA",
        "x-scope": "settings.read",
        "x-description-it": "Tutte le aliquote disponibili, con `enabled` a indicare quelle attive\nper l'officina.\n",
        "operationId": "vatRateCatalog",
        "description": "global catalog with the company's enabled marks.\n",
        "responses": {
          "200": {
            "description": "success",
            "x-description-it": "Il catalogo delle aliquote.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/json": {
                "example": {
                  "object": "list",
                  "data": [
                    {
                      "object": "vat_rate_catalog_entry",
                      "code": "22",
                      "rate": 22,
                      "description": "Aliquota ordinaria",
                      "enabled": true
                    },
                    {
                      "object": "vat_rate_catalog_entry",
                      "code": "10",
                      "rate": 10,
                      "description": "Aliquota ridotta",
                      "enabled": true
                    },
                    {
                      "object": "vat_rate_catalog_entry",
                      "code": "N2.2",
                      "rate": 0,
                      "description": "Non soggetta",
                      "enabled": false
                    }
                  ],
                  "has_more": false,
                  "url": "/v1/vat-rates/catalog"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/vehicle-field-settings": {
      "parameters": [
        {
          "$ref": "#/components/parameters/APIVersion"
        }
      ],
      "get": {
        "tags": [
          "Settings"
        ],
        "summary": "Retrieve a vehicle field settings",
        "x-summary-it": "Recupera i campi veicolo",
        "x-scope": "settings.read",
        "x-description-it": "Quali campi facoltativi della scheda veicolo sono visibili e quali\ncampi personalizzati sono definiti, incluso quali campi data\ngenerano un evento in agenda (`reminder_days_before`).\n",
        "operationId": "vehicleFieldSettingsGet",
        "responses": {
          "200": {
            "description": "success",
            "x-description-it": "La configurazione dei campi veicolo.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/json": {
                "example": {
                  "object": "vehicle_field_settings",
                  "field_visibility": {
                    "km": true,
                    "numero_telaio": true,
                    "transmission": false,
                    "engine_category": true,
                    "engine_power_kw": false,
                    "engine_displacement_ccm": false,
                    "vehicle_weight_kg": false,
                    "registration_date": true,
                    "last_inspection_date": true,
                    "next_inspection_date": true
                  },
                  "custom_fields": [
                    {
                      "id": "8a2d6f41-9c30-4e75-b168-3f7a5c2e9d04",
                      "name": "Colore",
                      "type": "string",
                      "reminder_days_before": null
                    },
                    {
                      "id": "3f9b1c07-52ea-4d18-9c44-1b6e08a7f2d3",
                      "name": "Scadenza ATP",
                      "type": "datetime",
                      "reminder_days_before": 0
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      },
      "put": {
        "tags": [
          "Settings"
        ],
        "summary": "Replace vehicle field settings",
        "x-summary-it": "Imposta i campi veicolo",
        "x-scope": "settings.write",
        "x-description-it": "Sostituisce l'intera configurazione: i campi personalizzati non\nelencati vengono rimossi.\n\nGli eventi in agenda gia' creati non vengono toccati: attivare o\ndisattivare `reminder_days_before` cambia solo cosa succede al\nprossimo salvataggio di un veicolo.\n",
        "operationId": "vehicleFieldSettingsPut",
        "description": "replaces visibility toggles and custom field definitions.\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "field_visibility"
                ],
                "properties": {
                  "field_visibility": {
                    "type": "object",
                    "description": "Quali campi facoltativi restano visibili nella scheda veicolo.\n",
                    "properties": {
                      "km": {
                        "type": "boolean"
                      },
                      "numero_telaio": {
                        "type": "boolean"
                      },
                      "transmission": {
                        "type": "boolean"
                      },
                      "engine_category": {
                        "type": "boolean"
                      },
                      "engine_power_kw": {
                        "type": "boolean"
                      },
                      "engine_displacement_ccm": {
                        "type": "boolean"
                      },
                      "vehicle_weight_kg": {
                        "type": "boolean"
                      },
                      "registration_date": {
                        "type": "boolean"
                      },
                      "last_inspection_date": {
                        "type": "boolean"
                      },
                      "next_inspection_date": {
                        "type": "boolean"
                      }
                    }
                  },
                  "custom_fields": {
                    "type": "array",
                    "description": "L'insieme completo dei campi personalizzati. Quelli non\nelencati vengono rimossi.\n",
                    "items": {
                      "type": "object",
                      "required": [
                        "name",
                        "type"
                      ],
                      "properties": {
                        "id": {
                          "type": "string"
                        },
                        "name": {
                          "type": "string"
                        },
                        "type": {
                          "type": "string",
                          "enum": [
                            "string",
                            "integer",
                            "datetime"
                          ]
                        },
                        "reminder_days_before": {
                          "type": "integer",
                          "nullable": true,
                          "description": "Genera un evento in agenda per la data di questo\ncampo. `null` (o assente) nessun evento, `0` il\ngiorno stesso della scadenza. Solo per i campi\n`datetime`: sugli altri tipi viene ignorato.\n\nOggi l'anticipo non e' configurabile: un valore\ndiverso da zero viene normalizzato a `0`.\n\nATTENZIONE: questo endpoint sostituisce l'intera\nconfigurazione, quindi un client che ricostruisce\n`custom_fields` senza questo campo spegne gli eventi\ngia' attivi. Rileggere le impostazioni con la GET e\nrispedire il valore per conservarlo.\n"
                        }
                      }
                    }
                  }
                }
              },
              "example": {
                "field_visibility": {
                  "km": true,
                  "numero_telaio": true,
                  "transmission": false,
                  "engine_category": true,
                  "engine_power_kw": false,
                  "engine_displacement_ccm": false,
                  "vehicle_weight_kg": false,
                  "registration_date": true,
                  "last_inspection_date": true,
                  "next_inspection_date": true
                },
                "custom_fields": [
                  {
                    "name": "Colore",
                    "type": "string"
                  },
                  {
                    "name": "Scadenza ATP",
                    "type": "datetime",
                    "reminder_days_before": 0
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "success",
            "x-description-it": "La configurazione aggiornata.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/json": {
                "example": {
                  "object": "vehicle_field_settings",
                  "field_visibility": {
                    "km": true,
                    "numero_telaio": true,
                    "transmission": false,
                    "engine_category": true,
                    "engine_power_kw": false,
                    "engine_displacement_ccm": false,
                    "vehicle_weight_kg": false,
                    "registration_date": true,
                    "last_inspection_date": true,
                    "next_inspection_date": true
                  },
                  "custom_fields": [
                    {
                      "id": "8a2d6f41-9c30-4e75-b168-3f7a5c2e9d04",
                      "name": "Colore",
                      "type": "string",
                      "reminder_days_before": null
                    },
                    {
                      "id": "3f9b1c07-52ea-4d18-9c44-1b6e08a7f2d3",
                      "name": "Scadenza ATP",
                      "type": "datetime",
                      "reminder_days_before": 0
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/MalformedRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/vehicle-sales": {
      "parameters": [
        {
          "$ref": "#/components/parameters/APIVersion"
        }
      ],
      "get": {
        "tags": [
          "Dealership"
        ],
        "summary": "List vehicle sales",
        "x-summary-it": "Elenca le vendite veicoli",
        "x-scope": "reports.read",
        "x-description-it": "Le vendite di veicoli, con margine (`total_profit`) e riferimento\nalla fattura emessa.\n",
        "operationId": "vehicleSaleList",
        "description": "q       substring match on customer / manufacturer / model / plate\nstatus  \"draft\" | \"signed\" | \"delivered\" (Bozza / Contratto firmato /\nConsegnata) — omitted returns every status (\"Tutte\")\nfrom, to, preset   date range (default last 12 months)\n",
        "parameters": [
          {
            "name": "from",
            "in": "query",
            "required": false,
            "description": "Inizio dell'intervallo, formato `YYYY-MM-DD`.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Numero di risultati per pagina.",
            "schema": {
              "type": "integer"
            }
          },
          {
            "$ref": "#/components/parameters/DatePreset"
          },
          {
            "name": "q",
            "in": "query",
            "required": false,
            "description": "Ricerca testuale libera.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "starting_after",
            "in": "query",
            "required": false,
            "description": "Cursore opaco della pagina precedente. Va restituito invariato.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "description": "Filtra per stato.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "to",
            "in": "query",
            "required": false,
            "description": "Fine dell'intervallo, formato `YYYY-MM-DD`.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "success",
            "x-description-it": "Le vendite veicoli.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/json": {
                "example": {
                  "object": "list",
                  "data": [
                    {
                      "object": "vehicle_sale",
                      "id": "5b9d3e70-2c41-48a6-9e15-7f2a6c8b4d13",
                      "seq_number": 24,
                      "status": "delivered",
                      "customer_id": "4c911f08-7713-4e83-bdf6-a0692a1b43fd",
                      "customer_name": "Mario Rossi",
                      "vehicle_name": "Fiat Panda",
                      "vehicle_license": "AB123CD",
                      "sale_date": "2026-06-19",
                      "total_after_tax": 6500.0,
                      "total_profit": 1820.0,
                      "invoice_id": "4412e7b9-0c35-4a18-92d7-5b6ea1f38c04",
                      "comments": null
                    }
                  ],
                  "has_more": false,
                  "url": "/v1/vehicle-sales",
                  "date_filter": {
                    "object": "date_filter",
                    "preset": "year",
                    "from": "2026-01-01",
                    "to": "2026-12-31"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/vehicle-sales/status-counts": {
      "parameters": [
        {
          "$ref": "#/components/parameters/APIVersion"
        }
      ],
      "get": {
        "tags": [
          "Dealership"
        ],
        "summary": "Vehicle sale status counts",
        "x-summary-it": "Conta le vendite per stato",
        "x-scope": "reports.read",
        "x-description-it": "Il numero di vendite in ciascuno stato, con gli stessi filtri\ndell'elenco.\n",
        "operationId": "vehicleSaleStatusCounts",
        "parameters": [
          {
            "name": "from",
            "in": "query",
            "required": false,
            "description": "Inizio dell'intervallo, formato `YYYY-MM-DD`.",
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/DatePreset"
          },
          {
            "name": "q",
            "in": "query",
            "required": false,
            "description": "Ricerca testuale libera.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "to",
            "in": "query",
            "required": false,
            "description": "Fine dell'intervallo, formato `YYYY-MM-DD`.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "success",
            "x-description-it": "Il numero di vendite per stato.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/json": {
                "example": {
                  "object": "vehicle_sale_status_counts",
                  "draft": 1,
                  "signed": 2,
                  "delivered": 21,
                  "date_filter": {
                    "object": "date_filter",
                    "preset": "year",
                    "from": "2026-01-01",
                    "to": "2026-12-31"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/vehicle-sales/{id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/APIVersion"
        },
        {
          "name": "id",
          "in": "path",
          "required": true,
          "description": "Identifier of the target resource.",
          "schema": {
            "type": "string"
          }
        }
      ],
      "get": {
        "tags": [
          "Dealership"
        ],
        "summary": "Retrieve a vehicle sale",
        "x-summary-it": "Recupera una vendita veicolo",
        "x-scope": "reports.read",
        "x-description-it": "Restituisce la singola vendita con tutte le sezioni del dettaglio:\nrighe di ricavo e di costo, il costo di acquisto del veicolo, il\nmargine, i giorni di giacenza, gli allegati, il veicolo e la fattura\ncollegata.\n\nIl margine è `total_profit`: è il valore memorizzato che leggono\nanche i report e la Situazione concessionaria. **Non** è\n`total_revenue` meno i due costi — quella colonna scorpora l'IVA\ndalle righe di ricavo accessorie. Va mostrato, mai ricalcolato,\naltrimenti il dato non coincide con le altre schermate.\n\n`vehicle` e `invoice` valgono null se la relativa lettura fallisce:\nuna vendita il cui acquisto o la cui fattura non sono leggibili\nresta comunque consultabile.\n\nIl 404 copre sia un id inesistente sia uno di un'altra officina.\n",
        "operationId": "vehicleSaleGet",
        "responses": {
          "200": {
            "description": "success",
            "x-description-it": "La vendita richiesta.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/json": {
                "example": {
                  "object": "vehicle_sale",
                  "id": "5b9d3e70-2c41-48a6-9e15-7f2a6c8b4d13",
                  "seq_number": 24,
                  "status": "delivered",
                  "customer_id": "4c911f08-7713-4e83-bdf6-a0692a1b43fd",
                  "customer_name": "Mario Rossi",
                  "vehicle_name": "Fiat Panda",
                  "vehicle_license": "AB123CD",
                  "sale_date": "2026-06-19",
                  "total_after_tax": 6500.0,
                  "total_profit": 1820.0,
                  "invoice_id": "4412e7b9-0c35-4a18-92d7-5b6ea1f38c04",
                  "comments": null,
                  "vehicle_stock_id": "3e9a7c12-5b48-40d6-8f27-1c4b9e2a6d35",
                  "sold_by_name": "Luca Bianchi",
                  "created_at": "2026-06-19T09:12:44Z",
                  "total_before_tax": 6450.0,
                  "total_tax": 50.0,
                  "total_discount": 0.0,
                  "total_revenue": 6500.0,
                  "total_sale_costs": 0.0,
                  "vehicle_total_cost": 4680.0,
                  "margin_percent": 28.0,
                  "days_in_stock": 92,
                  "revenue_line_items": [
                    {
                      "object": "vehicle_sale_revenue_line_item",
                      "id": "9f1b2c34-6d78-4e90-a1b2-c3d4e5f60718",
                      "category": "vehicle",
                      "description": "Vendita Fiat Panda",
                      "tax_rate": 0,
                      "vat_code": "N5",
                      "amount": 6500.0
                    }
                  ],
                  "cost_line_items": [],
                  "documents": [
                    {
                      "object": "vehicle_sale_document",
                      "id": "71c8d9e0-2f31-4a52-b6c7-8d9e0f1a2b3c",
                      "name": "contratto.pdf",
                      "content_type": "application/pdf",
                      "description": null,
                      "url": "/v1/vehicle-sales/5b9d3e70-2c41-48a6-9e15-7f2a6c8b4d13/documents/71c8d9e0-2f31-4a52-b6c7-8d9e0f1a2b3c/download",
                      "file_size_bytes": 184320,
                      "created_at": "2026-06-19T09:20:03Z"
                    }
                  ],
                  "vehicle": {
                    "object": "dealership_vehicle",
                    "id": "8821f0ac-5d2b-41e7-9a10-6c3f2b7d4e55",
                    "name": "Fiat Panda",
                    "manufacturer_name": "Fiat",
                    "model": "Panda",
                    "license": "AB123CD",
                    "vin": "ZFA31200003456789",
                    "km": 82000,
                    "registered_at": "2018-03-12"
                  },
                  "invoice": {
                    "object": "dealership_invoice",
                    "id": "4412e7b9-0c35-4a18-92d7-5b6ea1f38c04",
                    "seq_number": 118,
                    "issued_at": "2026-06-20"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/vehicle-sales/{id}/documents/{documentID}/download": {
      "parameters": [
        {
          "$ref": "#/components/parameters/APIVersion"
        },
        {
          "name": "id",
          "in": "path",
          "required": true,
          "description": "Identifier of the target resource.",
          "schema": {
            "type": "string"
          }
        },
        {
          "name": "documentID",
          "in": "path",
          "required": true,
          "description": "Identifier of the target resource.",
          "schema": {
            "type": "string"
          }
        }
      ],
      "get": {
        "tags": [
          "Dealership"
        ],
        "summary": "Download a vehicle sale attachment",
        "x-summary-it": "Scarica un allegato della vendita",
        "x-scope": "reports.read",
        "x-description-it": "Restituisce i byte di un allegato elencato in `documents` nel\ndettaglio della vendita. Il percorso di archiviazione è privato: il\nfile viene letto lato server e restituito con il proprio content\ntype.\n\nL'allegato deve appartenere sia alla vendita indicata nel path sia\nall'officina chiamante: controllare solo l'officina permetterebbe di\nusare l'id di una vendita per scaricare l'allegato di un'altra.\n\nCaricamento ed eliminazione restano sul web.\n",
        "operationId": "vehicleSaleDocumentDownload",
        "responses": {
          "200": {
            "description": "the file bytes",
            "x-description-it": "Il file richiesto.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/octet-stream": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/vehicle-stocks": {
      "parameters": [
        {
          "$ref": "#/components/parameters/APIVersion"
        }
      ],
      "get": {
        "tags": [
          "Dealership"
        ],
        "summary": "List vehicle stocks",
        "x-summary-it": "Elenca i veicoli in stock",
        "x-scope": "reports.read",
        "x-description-it": "I veicoli acquistati per la rivendita, con costo di acquisto, costo\ntotale sostenuto e giorni di giacenza.\n\nLa concessionaria rientra nel permesso `reports`, non ne ha uno\nproprio.\n",
        "operationId": "vehicleStockList",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Numero di risultati per pagina.",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "q",
            "in": "query",
            "required": false,
            "description": "Ricerca testuale libera.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "starting_after",
            "in": "query",
            "required": false,
            "description": "Cursore opaco della pagina precedente. Va restituito invariato.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "success",
            "x-description-it": "I veicoli in stock.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/json": {
                "example": {
                  "object": "list",
                  "data": [
                    {
                      "object": "vehicle_stock",
                      "id": "3e9a7c12-5b48-40d6-8f27-1c4b9e2a6d35",
                      "seq_number": 18,
                      "vehicle_id": "8821f0ac-5d2b-41e7-9a10-6c3f2b7d4e55",
                      "vehicle_name": "Fiat Panda",
                      "vehicle_license": "AB123CD",
                      "purchase_date": "2026-05-04",
                      "purchase_cost": 4200.0,
                      "total_cost": 4680.0,
                      "sale_price": 6500.0,
                      "days_in_stock": 92,
                      "sold": false
                    }
                  ],
                  "has_more": false,
                  "url": "/v1/vehicle-stocks"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      },
      "post": {
        "tags": [
          "Dealership"
        ],
        "summary": "Create a vehicle stock",
        "x-summary-it": "Registra un acquisto veicolo",
        "x-scope": "reports.write",
        "x-description-it": "Registra un nuovo acquisto veicolo. Crea anche il VEICOLO\ncorrispondente: un mezzo destinato al piazzale è per definizione\nnuovo per l'officina, quindi non esiste un veicolo a cui puntare —\nper questo i dati identificativi (`manufacturer_id`, `model`,\n`license`, `vin`, `registered_at`) stanno in questo corpo e non in un\n`vehicle_id`.\n\n`purchase_cost` e `total_cost` **non** si inviano: si ricavano da\n`line_items` — `purchase_cost` somma le righe con categoria\n\"purchase\", `total_cost` le somma tutte, come fa il form web.\nInviare totali propri farebbe divergere i valori memorizzati dalle\nrighe che li spiegano, perciò i campi non riconosciuti vengono\nrifiutati.\n\nAnche `line_items[].tax_rate` è assente: le righe di acquisto sono\nmemorizzate con aliquota 0 e `total_cost` è la somma lorda, quindi\nun'aliquota non cambierebbe nulla.\n\nRichiede il modulo Concessionaria (altrimenti 403). `purchase_date`\nnon può essere nel futuro; la data odierna è accettata. La targa\nviene normalizzata in maiuscolo.\n\n**Attenzione allo scope:** la concessionaria non ha un permesso\nproprio e ricade in `reports`, quindi questa scrittura richiede\n`reports.write` — che qui non scrive un report, registra un acquisto.\n\nLa modifica di un acquisto e l'intero ciclo di vita della vendita\nrestano sul web.\n\nRestituisce 201 con lo stesso payload di\n`GET /v1/vehicle-stocks/{id}`.\n\nL'acquisto creato **non** comparirà in `GET /v1/vehicle-stocks` se in\nseguito gli verrà associata una vendita: quell'elenco contiene solo i\nveicoli ancora disponibili.\n",
        "operationId": "vehicleStockCreate",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "manufacturer_id",
                  "model",
                  "license",
                  "purchase_date",
                  "line_items"
                ],
                "properties": {
                  "manufacturer_id": {
                    "type": "string",
                    "description": "Marca del veicolo."
                  },
                  "model": {
                    "type": "string",
                    "description": "Modello del veicolo."
                  },
                  "license": {
                    "type": "string",
                    "description": "Targa; normalizzata in maiuscolo."
                  },
                  "vin": {
                    "type": "string",
                    "nullable": true,
                    "description": "Numero di telaio."
                  },
                  "registered_at": {
                    "type": "string",
                    "format": "date-time",
                    "nullable": true,
                    "description": "Data di prima immatricolazione."
                  },
                  "purchase_date": {
                    "type": "string",
                    "format": "date-time",
                    "description": "Data di acquisto; non può essere nel futuro."
                  },
                  "supplier_id": {
                    "type": "string",
                    "nullable": true,
                    "description": "Fornitore da cui è stato acquistato."
                  },
                  "sold_by": {
                    "type": "string",
                    "nullable": true,
                    "description": "Collaboratore a cui è assegnata la vendita."
                  },
                  "comments": {
                    "type": "string",
                    "nullable": true,
                    "description": "Note libere."
                  },
                  "vehicle_sale_price": {
                    "type": "number",
                    "nullable": true,
                    "description": "Prezzo di vendita richiesto."
                  },
                  "line_items": {
                    "type": "array",
                    "minItems": 1,
                    "description": "Voci di costo che compongono total_cost.",
                    "items": {
                      "type": "object",
                      "required": [
                        "category",
                        "description",
                        "amount"
                      ],
                      "properties": {
                        "category": {
                          "type": "string",
                          "enum": [
                            "purchase",
                            "registration_fees",
                            "bodywork",
                            "refurbishment",
                            "tires",
                            "inspection",
                            "transport",
                            "cleaning",
                            "commissions",
                            "other"
                          ]
                        },
                        "description": {
                          "type": "string"
                        },
                        "amount": {
                          "type": "number",
                          "description": "Importo; non negativo."
                        }
                      }
                    }
                  }
                }
              },
              "example": {
                "manufacturer_id": "9c2e6b41-70a3-4d18-8f52-1b7d3c9a4e60",
                "model": "Giulietta",
                "license": "ZZ999ZZ",
                "vin": "ZAR94000007654321",
                "registered_at": "2019-04-10T00:00:00Z",
                "purchase_date": "2026-08-01T00:00:00Z",
                "supplier_id": "6d5c4b3a-2918-4706-b5c4-d3e2f1a09b87",
                "comments": "Ritirata da permuta",
                "vehicle_sale_price": 9500.0,
                "line_items": [
                  {
                    "category": "purchase",
                    "description": "Acquisto Giulietta",
                    "amount": 6000.0
                  },
                  {
                    "category": "bodywork",
                    "description": "Ritocco portiera",
                    "amount": 450.5
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "created",
            "x-description-it": "L'acquisto appena registrato.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/MalformedRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/vehicle-stocks/{id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/APIVersion"
        },
        {
          "name": "id",
          "in": "path",
          "required": true,
          "description": "Identifier of the target resource.",
          "schema": {
            "type": "string"
          }
        }
      ],
      "get": {
        "tags": [
          "Dealership"
        ],
        "summary": "Retrieve a vehicle stock",
        "x-summary-it": "Recupera un acquisto veicolo",
        "x-scope": "reports.read",
        "x-description-it": "Il dettaglio dell'acquisto. Aggiunge alla riga di elenco le voci di\ncosto che compongono `total_cost`, il fornitore, il veicolo, gli\nallegati e la vendita con cui il mezzo è uscito, se venduto.\n\nA differenza dell'elenco, qui si risolve anche un acquisto già\nvenduto: è l'unico modo per raggiungerlo, dato che l'elenco li\nesclude.\n\n`sold` è true quando esiste una vendita collegata. L'elenco ricava lo\nstesso flag da `vehicle_stocks.sold_by`, che il flusso di vendita non\nvalorizza sempre: il dettaglio riporta il dato più affidabile.\n\n`sale` non è la riga completa della vendita: la query per acquisto\nnon legge `total_after_tax`, quindi vengono esposti solo i campi\neffettivamente valorizzati. Per gli importi completi seguire\n`sale.id`.\n\nIl 404 copre sia un id inesistente sia uno di un'altra officina.\n",
        "operationId": "vehicleStockGet",
        "responses": {
          "200": {
            "description": "success",
            "x-description-it": "L'acquisto richiesto.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/json": {
                "example": {
                  "object": "vehicle_stock",
                  "id": "3e9a7c12-5b48-40d6-8f27-1c4b9e2a6d35",
                  "seq_number": 18,
                  "vehicle_id": "8821f0ac-5d2b-41e7-9a10-6c3f2b7d4e55",
                  "vehicle_name": "Fiat Panda",
                  "vehicle_license": "AB123CD",
                  "purchase_date": "2026-05-04",
                  "purchase_cost": 4200.0,
                  "total_cost": 4680.0,
                  "sale_price": 6500.0,
                  "days_in_stock": 92,
                  "sold": true,
                  "supplier_id": "6d5c4b3a-2918-4706-b5c4-d3e2f1a09b87",
                  "supplier_name": "Autoparco Bergamo",
                  "sold_by_name": "Luca Bianchi",
                  "comments": null,
                  "created_at": "2026-05-04T08:31:17Z",
                  "line_items": [
                    {
                      "object": "vehicle_stock_line_item",
                      "id": "a1b2c3d4-e5f6-4708-9a0b-1c2d3e4f5061",
                      "category": "purchase",
                      "description": "Acquisto Fiat Panda",
                      "tax_rate": 0,
                      "vat_code": null,
                      "amount": 4200.0
                    },
                    {
                      "object": "vehicle_stock_line_item",
                      "id": "b2c3d4e5-f607-4819-a0b1-2c3d4e5f6072",
                      "category": "bodywork",
                      "description": "Ritocco paraurti",
                      "tax_rate": 22,
                      "vat_code": null,
                      "amount": 480.0
                    }
                  ],
                  "documents": [],
                  "vehicle": {
                    "object": "dealership_vehicle",
                    "id": "8821f0ac-5d2b-41e7-9a10-6c3f2b7d4e55",
                    "name": "Fiat Panda",
                    "manufacturer_name": "Fiat",
                    "model": "Panda",
                    "license": "AB123CD",
                    "vin": "ZFA31200003456789",
                    "km": 82000,
                    "registered_at": "2018-03-12"
                  },
                  "sale": {
                    "object": "vehicle_sale_link",
                    "id": "5b9d3e70-2c41-48a6-9e15-7f2a6c8b4d13",
                    "seq_number": 24,
                    "status": "delivered",
                    "customer_id": "4c911f08-7713-4e83-bdf6-a0692a1b43fd",
                    "customer_name": "Mario Rossi",
                    "sale_date": "2026-06-19",
                    "sale_price": 6500.0,
                    "total_profit": 1820.0
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/vehicle-stocks/{id}/documents/{documentID}/download": {
      "parameters": [
        {
          "$ref": "#/components/parameters/APIVersion"
        },
        {
          "name": "id",
          "in": "path",
          "required": true,
          "description": "Identifier of the target resource.",
          "schema": {
            "type": "string"
          }
        },
        {
          "name": "documentID",
          "in": "path",
          "required": true,
          "description": "Identifier of the target resource.",
          "schema": {
            "type": "string"
          }
        }
      ],
      "get": {
        "tags": [
          "Dealership"
        ],
        "summary": "Download a vehicle stock attachment",
        "x-summary-it": "Scarica un allegato dell'acquisto",
        "x-scope": "reports.read",
        "x-description-it": "Restituisce i byte di un allegato elencato in `documents` nel\ndettaglio dell'acquisto, con le stesse regole della variante per la\nvendita: lettura lato server del percorso privato, content type\noriginale, e l'allegato deve appartenere sia all'acquisto indicato\nnel path sia all'officina chiamante.\n\nCaricamento ed eliminazione restano sul web.\n",
        "operationId": "vehicleStockDocumentDownload",
        "responses": {
          "200": {
            "description": "the file bytes",
            "x-description-it": "Il file richiesto.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/octet-stream": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/vehicles": {
      "parameters": [
        {
          "$ref": "#/components/parameters/APIVersion"
        }
      ],
      "get": {
        "tags": [
          "Vehicles"
        ],
        "summary": "List vehicles",
        "x-summary-it": "Elenca i veicoli",
        "x-scope": "vehicles.read",
        "x-description-it": "Restituisce i veicoli dell'officina, dal più recente. Filtra per\ntesto con `q` (modello o targa) e per proprietario con\n`customer_id`. La paginazione usa un cursore opaco: passa\n`next_starting_after` invariato in `starting_after` per la pagina\nsuccessiva e usa `has_more` per sapere se fermarti.\n",
        "description": "Returns vehicles in the authenticated user's company, newest\nfirst. Search by name with `?q=`; filter to a single customer\nwith `?customer_id=`; paginate with cursor-based `?limit=` +\n`?starting_after=`.\n\nThe cursor is opaque — clients MUST NOT parse it. Pass back\nthe value verbatim. Today it encodes an offset; tomorrow it\nmay encode a `(created_at, id)` tuple without a client-visible\nchange.\n\n`has_more` is `true` when more results exist beyond this page.\nDrive the \"load more\" UI off this flag, not off `data.length`\n— the last page may still contain a full `limit` worth of\nresults.\n",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": false,
            "description": "Substring match on vehicle model / license.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "customer_id",
            "in": "query",
            "required": false,
            "description": "Restrict the result set to vehicles owned by a single\ncustomer. Useful for the \"customer detail → vehicles\"\nmobile screen.\n",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "sort",
            "in": "query",
            "required": false,
            "description": "Sort key (default `date` = creation date). Named keys are\nmapped server-side to whitelisted columns.\n",
            "schema": {
              "type": "string",
              "enum": [
                "date",
                "customer",
                "vehicle",
                "license"
              ]
            }
          },
          {
            "name": "direction",
            "in": "query",
            "required": false,
            "description": "Sort direction (default `desc`).",
            "schema": {
              "type": "string",
              "enum": [
                "asc",
                "desc"
              ]
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Page size. Default 20, max 100. Out-of-range values clamp\nsilently to the defaults — never error.\n",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 20
            }
          },
          {
            "name": "starting_after",
            "in": "query",
            "required": false,
            "description": "Opaque cursor from a prior response's next-page link. Pass\nverbatim. Omit on the first page.\n",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Page of vehicles.",
            "x-description-it": "Una pagina di veicoli.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ListEnvelope"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/Vehicle"
                          }
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "object": "list",
                  "data": [
                    {
                      "object": "vehicle",
                      "id": "7b1f4a92-3c8d-4e15-9a20-5f6c8d3e1b47",
                      "license": "AB123CD",
                      "manufacturer_id": "c3d9e8f1-4a72-4b60-8e15-9d2f7c4a6b83",
                      "manufacturer_name": "Fiat",
                      "model": "Panda",
                      "variant": "1.2 Lounge",
                      "vin": "ZFA31200003456789",
                      "transmission": "manuale",
                      "engine_power_kw": 51,
                      "engine_displacement_ccm": 1242,
                      "engine_category": "Euro 6",
                      "registered_at": "2019-06-12T00:00:00Z",
                      "customer_id": "4c911f08-7713-4e83-bdf6-a0692a1b43fd",
                      "customer_name": "Mario Rossi",
                      "km": 84210,
                      "km_registered_at": "2026-07-14T09:12:00Z",
                      "last_inspection_at": "2025-06-01T00:00:00Z",
                      "next_inspection_at": "2027-06-01T00:00:00Z",
                      "is_rental": false,
                      "created_at": "2024-03-02T10:14:33Z",
                      "updated_at": "2026-07-14T09:12:00Z"
                    }
                  ],
                  "has_more": true,
                  "next_starting_after": "eyJvIjoyMH0",
                  "url": "/v1/vehicles"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMediaType"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      },
      "post": {
        "tags": [
          "Vehicles"
        ],
        "summary": "Create a vehicle",
        "x-summary-it": "Crea un veicolo",
        "x-scope": "vehicles.write",
        "x-description-it": "Crea un veicolo. Sono obbligatori `manufacturer_id` e `model`; la\nmarca deve essere fra quelle attive per l'officina (vedi\n`GET /v1/manufacturers`). Collega il veicolo a un cliente con\n`customer_id`: può restare vuoto e venire assegnato in seguito.\n",
        "description": "Creates a new vehicle in the authenticated user's company.\n`company_id` is always derived from the bearer token and\n`created_by` from the authenticated user — neither can be\nset from the body.\n\nRequired: `model`, `manufacturer_id`.\n\nRental fields (`is_rental`, `rental_*`) are not accepted on\ncreate; rental setup is a separate flow.\n\nCreating a vehicle also schedules its deadline events in the\nagenda: the revisione (`next_inspection_at`) and every custom\ndate field whose definition sets `reminder_days_before` (see\n`GET /v1/vehicle-field-settings`).\n\nReturns the fully materialized vehicle (including the joined\n`manufacturer_name` and `customer_name`).\n\nForeign-key references in the body (`customer_id`, etc.) must\nresolve to resources owned by the authenticated user's company.\nUnreachable refs return 422 `parameter_invalid` pointing at the\noffending field.\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/VehicleCreateRequest"
              },
              "example": {
                "manufacturer_id": "c3d9e8f1-4a72-4b60-8e15-9d2f7c4a6b83",
                "model": "Panda",
                "license": "AB123CD",
                "customer_id": "4c911f08-7713-4e83-bdf6-a0692a1b43fd",
                "km": 84210.5
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Vehicle created.",
            "x-description-it": "Il veicolo creato.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Vehicle"
                },
                "example": {
                  "object": "vehicle",
                  "id": "7b1f4a92-3c8d-4e15-9a20-5f6c8d3e1b47",
                  "license": "AB123CD",
                  "manufacturer_id": "c3d9e8f1-4a72-4b60-8e15-9d2f7c4a6b83",
                  "manufacturer_name": "Fiat",
                  "model": "Panda",
                  "variant": "1.2 Lounge",
                  "vin": "ZFA31200003456789",
                  "transmission": "manuale",
                  "engine_power_kw": 51,
                  "engine_displacement_ccm": 1242,
                  "engine_category": "Euro 6",
                  "registered_at": "2019-06-12T00:00:00Z",
                  "customer_id": "4c911f08-7713-4e83-bdf6-a0692a1b43fd",
                  "customer_name": "Mario Rossi",
                  "km": 84210,
                  "km_registered_at": "2026-07-14T09:12:00Z",
                  "last_inspection_at": "2025-06-01T00:00:00Z",
                  "next_inspection_at": "2027-06-01T00:00:00Z",
                  "is_rental": false,
                  "created_at": "2024-03-02T10:14:33Z",
                  "updated_at": "2026-07-14T09:12:00Z"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/MalformedRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMediaType"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/vehicles/{id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/APIVersion"
        },
        {
          "name": "id",
          "in": "path",
          "required": true,
          "description": "Vehicle id (UUID).",
          "schema": {
            "type": "string"
          }
        }
      ],
      "get": {
        "tags": [
          "Vehicles"
        ],
        "summary": "Retrieve a vehicle",
        "x-summary-it": "Recupera un veicolo",
        "x-scope": "vehicles.read",
        "x-description-it": "Restituisce un singolo veicolo. Rispetto all'elenco include anche i\ndocumenti allegati, i campi personalizzati risolti\n(`custom_field_values`) e il primo appuntamento in agenda\n(`next_appointment`).\n",
        "description": "Returns a single vehicle by id, scoped to the authenticated\nuser's company. Unknown ids, ids from another company, and\nsoft-deleted rows all return 404 — never 403, never 200 with\ndeleted_at populated.\n",
        "responses": {
          "200": {
            "description": "Vehicle found.",
            "x-description-it": "Il veicolo richiesto.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Vehicle"
                },
                "example": {
                  "object": "vehicle",
                  "id": "7b1f4a92-3c8d-4e15-9a20-5f6c8d3e1b47",
                  "license": "AB123CD",
                  "manufacturer_id": "c3d9e8f1-4a72-4b60-8e15-9d2f7c4a6b83",
                  "manufacturer_name": "Fiat",
                  "model": "Panda",
                  "variant": "1.2 Lounge",
                  "vin": "ZFA31200003456789",
                  "transmission": "manuale",
                  "engine_power_kw": 51,
                  "engine_displacement_ccm": 1242,
                  "engine_category": "Euro 6",
                  "registered_at": "2019-06-12T00:00:00Z",
                  "customer_id": "4c911f08-7713-4e83-bdf6-a0692a1b43fd",
                  "customer_name": "Mario Rossi",
                  "km": 84210,
                  "km_registered_at": "2026-07-14T09:12:00Z",
                  "last_inspection_at": "2025-06-01T00:00:00Z",
                  "next_inspection_at": "2027-06-01T00:00:00Z",
                  "is_rental": false,
                  "created_at": "2024-03-02T10:14:33Z",
                  "updated_at": "2026-07-14T09:12:00Z",
                  "next_appointment": null,
                  "comments": "Cliente chiede sempre olio Selenia.",
                  "documents": [
                    {
                      "object": "vehicle_document",
                      "id": "e2a4b6c8-1d3f-4059-b7e8-2c9a5d1f3b64",
                      "name": "libretto.pdf",
                      "content_type": "application/pdf",
                      "file_size_bytes": 148213,
                      "url": "/v1/vehicles/7b1f4a92-3c8d-4e15-9a20-5f6c8d3e1b47/documents/e2a4b6c8-1d3f-4059-b7e8-2c9a5d1f3b64/download",
                      "created_at": "2026-07-14T09:20:11Z"
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMediaType"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      },
      "patch": {
        "tags": [
          "Vehicles"
        ],
        "summary": "Update a vehicle",
        "x-summary-it": "Aggiorna un veicolo",
        "x-scope": "vehicles.write",
        "x-description-it": "Aggiorna solo i campi presenti nel corpo della richiesta: i campi\nomessi restano invariati. Per svuotare un campo passalo\nesplicitamente a `null`.\n",
        "description": "Partial update following the design philosophy:\n\n- **Omitted JSON keys** leave the corresponding entity fields\n  untouched.\n- **Explicit JSON `null`** clears the field (SQL NULL).\n- **Concrete values** overwrite the entity field.\n\n`model` and `manufacturer_id` are NOT NULL in the schema;\nsending either as `null` is a 422 `parameter_invalid` error.\n\n`custom_fields` is patched as a whole-object replacement\n(omit = unchanged, null = clear, object = overwrite). Per-key\nmerging is intentionally not supported.\n\nDeadline dates keep their agenda events in step: moving a date\nmoves its event, clearing one removes it, and a request that\ndoes not change a date touches no event. This covers\n`next_inspection_at` and every custom date field whose\ndefinition sets `reminder_days_before`. Because `custom_fields`\nis replaced wholesale, omitting a key that held a date clears\nit — and removes that event.\n\nRental fields are read-only here — use the rental setup flow\nto change them.\n\nReturns the fully materialized post-update vehicle.\n\nForeign-key references in the body (`customer_id`, etc.) must\nresolve to resources owned by the authenticated user's company.\nUnreachable refs return 422 `parameter_invalid` pointing at the\noffending field.\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/VehiclePatchRequest"
              },
              "example": {
                "license": "EF456GH",
                "km": 90100,
                "customer_id": null
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Vehicle updated. Returns the full post-update entity.",
            "x-description-it": "Il veicolo aggiornato.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Vehicle"
                },
                "example": {
                  "object": "vehicle",
                  "id": "7b1f4a92-3c8d-4e15-9a20-5f6c8d3e1b47",
                  "license": "AB123CD",
                  "manufacturer_id": "c3d9e8f1-4a72-4b60-8e15-9d2f7c4a6b83",
                  "manufacturer_name": "Fiat",
                  "model": "Panda",
                  "variant": "1.2 Lounge",
                  "vin": "ZFA31200003456789",
                  "transmission": "manuale",
                  "engine_power_kw": 51,
                  "engine_displacement_ccm": 1242,
                  "engine_category": "Euro 6",
                  "registered_at": "2019-06-12T00:00:00Z",
                  "customer_id": "4c911f08-7713-4e83-bdf6-a0692a1b43fd",
                  "customer_name": "Mario Rossi",
                  "km": 84210,
                  "km_registered_at": "2026-07-14T09:12:00Z",
                  "last_inspection_at": "2025-06-01T00:00:00Z",
                  "next_inspection_at": "2027-06-01T00:00:00Z",
                  "is_rental": false,
                  "created_at": "2024-03-02T10:14:33Z",
                  "updated_at": "2026-07-14T09:12:00Z",
                  "next_appointment": null,
                  "comments": "Cliente chiede sempre olio Selenia.",
                  "documents": [
                    {
                      "object": "vehicle_document",
                      "id": "e2a4b6c8-1d3f-4059-b7e8-2c9a5d1f3b64",
                      "name": "libretto.pdf",
                      "content_type": "application/pdf",
                      "file_size_bytes": 148213,
                      "url": "/v1/vehicles/7b1f4a92-3c8d-4e15-9a20-5f6c8d3e1b47/documents/e2a4b6c8-1d3f-4059-b7e8-2c9a5d1f3b64/download",
                      "created_at": "2026-07-14T09:20:11Z"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/MalformedRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMediaType"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      },
      "delete": {
        "tags": [
          "Vehicles"
        ],
        "summary": "Soft-delete a vehicle",
        "x-summary-it": "Elimina un veicolo",
        "x-scope": "vehicles.write",
        "x-description-it": "Eliminazione logica: il veicolo sparisce dagli elenchi ma i documenti\ngià emessi che lo citano restano intatti.\n",
        "description": "Marks the vehicle as deleted by stamping `deleted_at` with the\nserver clock. The row stays in the database; subsequent\nGET/PATCH/DELETE all 404.\n\nReturns the deleted resource (200) rather than 204 No Content\nso the response shape is consistent across the API and the\nmobile UI can render an undo banner with the timestamp.\n",
        "responses": {
          "200": {
            "description": "Vehicle soft-deleted. `deleted_at` is populated.",
            "x-description-it": "Il veicolo eliminato.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Vehicle"
                },
                "example": {
                  "object": "vehicle",
                  "id": "7b1f4a92-3c8d-4e15-9a20-5f6c8d3e1b47",
                  "license": "AB123CD",
                  "manufacturer_id": "c3d9e8f1-4a72-4b60-8e15-9d2f7c4a6b83",
                  "manufacturer_name": "Fiat",
                  "model": "Panda",
                  "variant": "1.2 Lounge",
                  "vin": "ZFA31200003456789",
                  "transmission": "manuale",
                  "engine_power_kw": 51,
                  "engine_displacement_ccm": 1242,
                  "engine_category": "Euro 6",
                  "registered_at": "2019-06-12T00:00:00Z",
                  "customer_id": "4c911f08-7713-4e83-bdf6-a0692a1b43fd",
                  "customer_name": "Mario Rossi",
                  "km": 84210,
                  "km_registered_at": "2026-07-14T09:12:00Z",
                  "last_inspection_at": "2025-06-01T00:00:00Z",
                  "next_inspection_at": "2027-06-01T00:00:00Z",
                  "is_rental": false,
                  "created_at": "2024-03-02T10:14:33Z",
                  "updated_at": "2026-07-14T09:12:00Z",
                  "deleted_at": "2026-07-20T11:04:00Z"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/vehicles/{id}/checkins": {
      "parameters": [
        {
          "$ref": "#/components/parameters/APIVersion"
        },
        {
          "name": "id",
          "in": "path",
          "required": true,
          "description": "Vehicle id (UUID).",
          "schema": {
            "type": "string"
          }
        }
      ],
      "get": {
        "tags": [
          "Vehicles"
        ],
        "summary": "Check-in (Accettazioni) history for a vehicle",
        "x-summary-it": "Storico accettazioni del veicolo",
        "x-scope": "checkins.read",
        "x-description-it": "Le accettazioni registrate per il veicolo, dalla più recente.\nRichiede il permesso `checkins.read`.\n",
        "description": "Every check-in linked to the vehicle, newest first, without\nline items. Unpaginated (`has_more` always false).\n",
        "responses": {
          "200": {
            "description": "The vehicle's check-ins.",
            "x-description-it": "Le accettazioni del veicolo.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "object",
                    "data",
                    "has_more",
                    "url"
                  ],
                  "properties": {
                    "object": {
                      "type": "string",
                      "const": "list"
                    },
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Checkin"
                      }
                    },
                    "has_more": {
                      "type": "boolean",
                      "const": false
                    },
                    "url": {
                      "type": "string"
                    }
                  }
                },
                "example": {
                  "object": "list",
                  "data": [
                    {
                      "object": "checkin",
                      "id": "1c7a9e04-5f83-4b26-8d10-6e2b4a9c7f31",
                      "status": "convertita",
                      "vehicle_id": "7b1f4a92-3c8d-4e15-9a20-5f6c8d3e1b47",
                      "customer_id": "4c911f08-7713-4e83-bdf6-a0692a1b43fd",
                      "created_at": "2026-05-17T16:44:00Z"
                    }
                  ],
                  "has_more": false,
                  "url": "/v1/vehicles/7b1f4a92-3c8d-4e15-9a20-5f6c8d3e1b47/checkins"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/vehicles/{id}/documents": {
      "parameters": [
        {
          "$ref": "#/components/parameters/APIVersion"
        },
        {
          "name": "id",
          "in": "path",
          "required": true,
          "description": "Vehicle id (UUID).",
          "schema": {
            "type": "string"
          }
        }
      ],
      "post": {
        "tags": [
          "Vehicles"
        ],
        "summary": "Upload a document to a vehicle",
        "x-summary-it": "Allega un documento al veicolo",
        "x-scope": "vehicles.write",
        "x-description-it": "Carica un file fra i Documenti Allegati del veicolo. La richiesta e'\n`multipart/form-data` con il file nel campo `file`, non JSON.\n",
        "description": "`multipart/form-data` — mirrors the customer document upload:\npart `file` (required, max 25MB) and field `description`\n(display label; the app requires it, the server falls back to\nthe file name — web form parity). Storage layout matches the\nweb upload, so documents from either surface are identical.\n",
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "required": [
                  "file"
                ],
                "properties": {
                  "file": {
                    "type": "string",
                    "format": "binary"
                  },
                  "description": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The stored document.",
            "x-description-it": "Il documento allegato.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VehicleDocument"
                },
                "example": {
                  "object": "vehicle_document",
                  "id": "e2a4b6c8-1d3f-4059-b7e8-2c9a5d1f3b64",
                  "name": "libretto.pdf",
                  "content_type": "application/pdf",
                  "description": "Carta di circolazione",
                  "file_size_bytes": 148213,
                  "url": "/v1/vehicles/7b1f4a92-3c8d-4e15-9a20-5f6c8d3e1b47/documents/e2a4b6c8-1d3f-4059-b7e8-2c9a5d1f3b64/download",
                  "created_at": "2026-07-14T09:20:11Z"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/vehicles/{id}/documents/{documentID}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/APIVersion"
        },
        {
          "name": "id",
          "in": "path",
          "required": true,
          "description": "Identifier of the target resource.",
          "schema": {
            "type": "string"
          }
        },
        {
          "name": "documentID",
          "in": "path",
          "required": true,
          "description": "Identifier of the target resource.",
          "schema": {
            "type": "string"
          }
        }
      ],
      "delete": {
        "tags": [
          "Vehicles"
        ],
        "summary": "Delete vehicle document",
        "x-summary-it": "Elimina un documento del veicolo",
        "x-scope": "vehicles.write",
        "x-description-it": "Rimuove definitivamente il file allegato.\n",
        "operationId": "vehicleDocumentDelete",
        "description": "VehicleDocumentDelete handles\nDELETE /v1/vehicles/{id}/documents/{documentID}.\n",
        "responses": {
          "200": {
            "description": "success (the removed document)",
            "x-description-it": "Il documento eliminato.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VehicleDocument"
                },
                "example": {
                  "object": "vehicle_document",
                  "id": "e2a4b6c8-1d3f-4059-b7e8-2c9a5d1f3b64",
                  "name": "libretto.pdf",
                  "content_type": "application/pdf",
                  "file_size_bytes": 148213,
                  "created_at": "2026-07-14T09:20:11Z"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/vehicles/{id}/documents/{documentID}/download": {
      "parameters": [
        {
          "$ref": "#/components/parameters/APIVersion"
        },
        {
          "name": "id",
          "in": "path",
          "required": true,
          "description": "Vehicle id (UUID).",
          "schema": {
            "type": "string"
          }
        },
        {
          "name": "documentID",
          "in": "path",
          "required": true,
          "description": "Document id (UUID).",
          "schema": {
            "type": "string"
          }
        }
      ],
      "get": {
        "tags": [
          "Vehicles"
        ],
        "summary": "Download a vehicle document",
        "x-summary-it": "Scarica un documento del veicolo",
        "x-scope": "vehicles.read",
        "x-description-it": "Restituisce il contenuto del file, non JSON. È il percorso indicato\ndal campo `url` di ogni documento.\n",
        "description": "Streams the stored bytes with the document's Content-Type and\nan attachment Content-Disposition. The target of every\nvehicle_document `url`.\n",
        "responses": {
          "200": {
            "description": "The file bytes.",
            "content": {
              "application/octet-stream": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/vehicles/{id}/quotes": {
      "parameters": [
        {
          "$ref": "#/components/parameters/APIVersion"
        },
        {
          "name": "id",
          "in": "path",
          "required": true,
          "description": "Vehicle id (UUID).",
          "schema": {
            "type": "string"
          }
        }
      ],
      "get": {
        "tags": [
          "Vehicles"
        ],
        "summary": "Quote history for a vehicle",
        "x-summary-it": "Storico preventivi del veicolo",
        "x-scope": "quotes.read",
        "x-description-it": "I preventivi collegati al veicolo, dal più recente. Richiede il\npermesso `quotes.read`.\n",
        "description": "Every quote linked to the vehicle, newest first, without line\nitems. Unpaginated (`has_more` always false).\n",
        "responses": {
          "200": {
            "description": "The vehicle's quotes.",
            "x-description-it": "I preventivi del veicolo.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "object",
                    "data",
                    "has_more",
                    "url"
                  ],
                  "properties": {
                    "object": {
                      "type": "string",
                      "const": "list"
                    },
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Quote"
                      }
                    },
                    "has_more": {
                      "type": "boolean",
                      "const": false
                    },
                    "url": {
                      "type": "string"
                    }
                  }
                },
                "example": {
                  "object": "list",
                  "data": [
                    {
                      "object": "quote",
                      "id": "8d3f5b92-2a71-4c08-9e6b-1f4a7d0c3e85",
                      "seq_number": 87,
                      "status": "accettato",
                      "vehicle_id": "7b1f4a92-3c8d-4e15-9a20-5f6c8d3e1b47",
                      "customer_id": "4c911f08-7713-4e83-bdf6-a0692a1b43fd",
                      "total_after_tax": 310.0,
                      "created_at": "2026-04-02T11:05:00Z"
                    }
                  ],
                  "has_more": false,
                  "url": "/v1/vehicles/7b1f4a92-3c8d-4e15-9a20-5f6c8d3e1b47/quotes"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/vehicles/{id}/worksheets": {
      "parameters": [
        {
          "$ref": "#/components/parameters/APIVersion"
        },
        {
          "name": "id",
          "in": "path",
          "required": true,
          "description": "Vehicle id (UUID).",
          "schema": {
            "type": "string"
          }
        }
      ],
      "get": {
        "tags": [
          "Vehicles"
        ],
        "summary": "Worksheet history for a vehicle",
        "x-summary-it": "Storico schede lavoro del veicolo",
        "x-scope": "worksheets.read",
        "x-description-it": "Le schede lavoro del veicolo, dalla più recente. Restituisce schede\nlavoro, quindi richiede il permesso `worksheets.read` e non\n`vehicles.read`.\n",
        "description": "Every worksheet linked to the vehicle, newest first, without\nline items. Unpaginated (`has_more` always false) — web\nvehicle-page parity.\n",
        "responses": {
          "200": {
            "description": "The vehicle's worksheets.",
            "x-description-it": "Le schede lavoro del veicolo.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "object",
                    "data",
                    "has_more",
                    "url"
                  ],
                  "properties": {
                    "object": {
                      "type": "string",
                      "const": "list"
                    },
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Worksheet"
                      }
                    },
                    "has_more": {
                      "type": "boolean",
                      "const": false
                    },
                    "url": {
                      "type": "string"
                    }
                  }
                },
                "example": {
                  "object": "list",
                  "data": [
                    {
                      "object": "worksheet",
                      "id": "5e8c1a37-9d24-4f60-b3a8-7c2e9f1d4b05",
                      "seq_number": 412,
                      "status": "completata",
                      "vehicle_id": "7b1f4a92-3c8d-4e15-9a20-5f6c8d3e1b47",
                      "customer_id": "4c911f08-7713-4e83-bdf6-a0692a1b43fd",
                      "customer_name": "Mario Rossi",
                      "total_after_tax": 285.4,
                      "created_at": "2026-05-18T08:30:00Z"
                    }
                  ],
                  "has_more": false,
                  "url": "/v1/vehicles/7b1f4a92-3c8d-4e15-9a20-5f6c8d3e1b47/worksheets"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/work-categories": {
      "parameters": [
        {
          "$ref": "#/components/parameters/APIVersion"
        }
      ],
      "get": {
        "tags": [
          "Worksheets"
        ],
        "summary": "List work categories (Tipo intervento)",
        "x-summary-it": "Elenca i tipi di intervento",
        "x-scope": "settings.read",
        "x-description-it": "I tipi di intervento configurati dall'officina, da usare per\nvalorizzare `work_category_id`. È una voce di Impostazioni, quindi\nrichiede il permesso `settings.read`.\n",
        "description": "The company's work-category catalog behind the worksheet and\nquote forms. Intentionally unpaginated: the set is small and\ncompany-curated. `has_more` is always `false`.\n",
        "responses": {
          "200": {
            "description": "Work categories.",
            "x-description-it": "I tipi di intervento configurati.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "object",
                    "data",
                    "has_more",
                    "url"
                  ],
                  "properties": {
                    "object": {
                      "type": "string",
                      "const": "list"
                    },
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/WorkCategory"
                      }
                    },
                    "has_more": {
                      "type": "boolean",
                      "const": false
                    },
                    "url": {
                      "type": "string"
                    }
                  }
                },
                "example": {
                  "object": "list",
                  "data": [
                    {
                      "object": "work_category",
                      "id": "3a8e5c21-9d76-4f03-b418-2c6a9e7d5b40",
                      "name": "Tagliando"
                    },
                    {
                      "object": "work_category",
                      "id": "5f2b7d94-1a63-48ec-9207-8d4e3c1f6a52",
                      "name": "Freni"
                    }
                  ],
                  "has_more": false,
                  "url": "/v1/work-categories"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      },
      "post": {
        "tags": [
          "Settings"
        ],
        "summary": "Create a work category",
        "x-summary-it": "Crea un tipo di intervento",
        "x-scope": "settings.write",
        "x-description-it": "Crea un tipo di intervento, usato per classificare schede lavoro e\npreventivi.\n",
        "operationId": "workCategoryCreate",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "name"
                ],
                "properties": {
                  "name": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "name": "Nuovo nome"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "success",
            "x-description-it": "Il tipo di intervento creato.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/json": {
                "example": {
                  "object": "work_category",
                  "id": "3a8e5c21-9d76-4f03-b418-2c6a9e7d5b40",
                  "name": "Tagliando"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/work-categories/{id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/APIVersion"
        },
        {
          "name": "id",
          "in": "path",
          "required": true,
          "description": "Identifier of the target resource.",
          "schema": {
            "type": "string"
          }
        }
      ],
      "delete": {
        "tags": [
          "Settings"
        ],
        "summary": "Delete work category",
        "x-summary-it": "Elimina un tipo di intervento",
        "x-scope": "settings.write",
        "x-description-it": "Elimina il tipo di intervento. I documenti che lo citano restano\nintatti.\n",
        "operationId": "workCategoryDelete",
        "responses": {
          "200": {
            "description": "success",
            "x-description-it": "Il tipo di intervento eliminato.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/json": {
                "example": {
                  "object": "work_category",
                  "id": "3a8e5c21-9d76-4f03-b418-2c6a9e7d5b40",
                  "name": "Tagliando"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      },
      "patch": {
        "tags": [
          "Settings"
        ],
        "summary": "Update work category",
        "x-summary-it": "Aggiorna un tipo di intervento",
        "x-scope": "settings.write",
        "x-description-it": "Aggiorna il nome del tipo di intervento.\n",
        "operationId": "workCategoryPatch",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "name"
                ],
                "properties": {
                  "name": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "name": "Nuovo nome"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "success",
            "x-description-it": "Il tipo di intervento aggiornato.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/json": {
                "example": {
                  "object": "work_category",
                  "id": "3a8e5c21-9d76-4f03-b418-2c6a9e7d5b40",
                  "name": "Tagliando"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/worksheets": {
      "parameters": [
        {
          "$ref": "#/components/parameters/APIVersion"
        }
      ],
      "get": {
        "tags": [
          "Worksheets"
        ],
        "summary": "List worksheets",
        "x-summary-it": "Elenca le schede lavoro",
        "x-scope": "worksheets.read",
        "x-description-it": "Le schede lavoro dell'officina, dalla più recente. Filtra per stato\ncon `status`, per cliente con `customer_id`, per veicolo con\n`vehicle_id` e per periodo con `from` / `to`. La paginazione usa un\ncursore opaco: passa `next_starting_after` invariato in\n`starting_after` e fermati quando `has_more` è `false`.\n",
        "description": "Returns worksheets in the authenticated user's company, newest\nfirst. Filter by status with `?status=`, by customer with\n`?customer_id=`, search customer names with `?q=`, and bound by\n`created_at` with `?from=` / `?to=` / `?preset=`. Paginate with\ncursor-based `?limit=` + `?starting_after=`.\n\nThe date window defaults to the last 12 months; set it with\n`from`/`to`/`preset`. The applied range is echoed in\n`date_filter`. Pass `?preset=all`\nto list across all time.\n\nLine items are **not** included in the list shape — fetch the\ndetail endpoint when full line-item data is needed.\n\nThe cursor is opaque — clients MUST NOT parse it. Pass back\nthe value verbatim.\n",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": false,
            "description": "Substring match on the customer's full name.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "description": "Filter to a single lifecycle state. Sending an unrecognized\nvalue returns 422 instead of silently coercing.\n",
            "schema": {
              "type": "string",
              "enum": [
                "draft",
                "waiting",
                "started",
                "completed",
                "delivered",
                "archived"
              ]
            }
          },
          {
            "name": "customer_id",
            "in": "query",
            "required": false,
            "description": "Restrict results to a single customer's worksheets.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "sort",
            "in": "query",
            "required": false,
            "description": "Sort key (default `date` = creation date). Named keys are\nmapped server-side to whitelisted columns.\n",
            "schema": {
              "type": "string",
              "enum": [
                "date",
                "number",
                "customer",
                "vehicle",
                "total",
                "outstanding"
              ]
            }
          },
          {
            "name": "direction",
            "in": "query",
            "required": false,
            "description": "Sort direction (default `desc`).",
            "schema": {
              "type": "string",
              "enum": [
                "asc",
                "desc"
              ]
            }
          },
          {
            "name": "from",
            "in": "query",
            "required": false,
            "description": "Inclusive lower bound on `created_at` (YYYY-MM-DD).",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "to",
            "in": "query",
            "required": false,
            "description": "Inclusive upper bound on `created_at` (YYYY-MM-DD).",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "$ref": "#/components/parameters/DatePreset"
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Page size. Default 20, max 100.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 20
            }
          },
          {
            "name": "starting_after",
            "in": "query",
            "required": false,
            "description": "Opaque cursor from a prior response. Omit on the first page.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Page of worksheets (without line items).",
            "x-description-it": "Una pagina di schede lavoro.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ListEnvelope"
                    },
                    {
                      "type": "object",
                      "required": [
                        "date_filter"
                      ],
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/Worksheet"
                          }
                        },
                        "date_filter": {
                          "$ref": "#/components/schemas/DateFilter"
                        }
                      }
                    }
                  ]
                },
                "example": {
                  "object": "list",
                  "data": [
                    {
                      "object": "worksheet",
                      "id": "a5d21e64-8f37-4b90-ae12-3c7d6b048f95",
                      "seq_number": 412,
                      "status": "started",
                      "title": "Tagliando 60.000 km",
                      "customer_id": "4c911f08-7713-4e83-bdf6-a0692a1b43fd",
                      "customer_name": "Mario Rossi",
                      "vehicle_id": "8821f0ac-5d2b-41e7-9a10-6c3f2b7d4e55",
                      "vehicle_name": "Fiat Panda",
                      "vehicle_license": "AB123CD",
                      "work_category_id": "3a8e5c21-9d76-4f03-b418-2c6a9e7d5b40",
                      "work_category_name": "Tagliando",
                      "km": 61240,
                      "total_before_tax": 233.9,
                      "total_tax": 51.46,
                      "total_after_tax": 285.36,
                      "total_discount": 0,
                      "total_payments": 150.0,
                      "outstanding_payments": 135.36,
                      "fiscal_document_type": "fattura",
                      "frozen": false,
                      "invoice_id": null,
                      "receipt_id": null,
                      "started_at": "2026-07-15T08:30:00Z",
                      "completed_at": null,
                      "delivered_at": null,
                      "created_at": "2026-07-15T08:12:00Z",
                      "updated_at": "2026-07-16T10:02:00Z"
                    }
                  ],
                  "has_more": true,
                  "next_starting_after": "eyJvIjoyMH0",
                  "url": "/v1/worksheets",
                  "date_filter": {
                    "object": "date_filter",
                    "preset": "year",
                    "from": "2026-01-01",
                    "to": "2026-12-31"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMediaType"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      },
      "post": {
        "tags": [
          "Worksheets"
        ],
        "summary": "Create a worksheet",
        "x-summary-it": "Crea una scheda lavoro",
        "x-scope": "worksheets.write",
        "x-description-it": "Crea una scheda lavoro in stato `draft`. Il numero progressivo\n(`seq_number`) è assegnato dal server. Le righe non si passano qui:\ncrea la scheda, poi aggiungile con\n`POST /v1/worksheets/{id}/line-items/batch`.\n",
        "description": "Creates a new worksheet, owned by the authenticated user's\ncompany. Server-controlled fields (`id`, `company_id`,\n`created_by`, `seq_number`, `status`, all `*_at` timestamps,\ntotals) are filled in automatically. Status always starts\nas `draft`.\n\n`customer_id` is required. Line items are not accepted at\ncreation time — a dedicated sub-resource will add/remove\nthem in a later pass.\n\nForeign-key references in the body (`customer_id`, `vehicle_id`,\n`work_category_id`, `assigned_to`) must resolve to resources\nowned by the authenticated user's company. Unreachable refs\nreturn 422 `parameter_invalid` pointing at the offending field.\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WorksheetCreateRequest"
              },
              "example": {
                "customer_id": "4c911f08-7713-4e83-bdf6-a0692a1b43fd",
                "vehicle_id": "8821f0ac-5d2b-41e7-9a10-6c3f2b7d4e55",
                "title": "Tagliando 60.000 km",
                "km": 61240
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Worksheet created. Returns the fully-materialized resource.",
            "x-description-it": "La scheda lavoro creata.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Worksheet"
                },
                "example": {
                  "object": "worksheet",
                  "id": "a5d21e64-8f37-4b90-ae12-3c7d6b048f95",
                  "seq_number": 412,
                  "status": "started",
                  "title": "Tagliando 60.000 km",
                  "customer_id": "4c911f08-7713-4e83-bdf6-a0692a1b43fd",
                  "customer_name": "Mario Rossi",
                  "vehicle_id": "8821f0ac-5d2b-41e7-9a10-6c3f2b7d4e55",
                  "vehicle_name": "Fiat Panda",
                  "vehicle_license": "AB123CD",
                  "work_category_id": "3a8e5c21-9d76-4f03-b418-2c6a9e7d5b40",
                  "work_category_name": "Tagliando",
                  "km": 61240,
                  "total_before_tax": 233.9,
                  "total_tax": 51.46,
                  "total_after_tax": 285.36,
                  "total_discount": 0,
                  "total_payments": 150.0,
                  "outstanding_payments": 135.36,
                  "fiscal_document_type": "fattura",
                  "frozen": false,
                  "invoice_id": null,
                  "receipt_id": null,
                  "started_at": "2026-07-15T08:30:00Z",
                  "completed_at": null,
                  "delivered_at": null,
                  "created_at": "2026-07-15T08:12:00Z",
                  "updated_at": "2026-07-16T10:02:00Z"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/MalformedRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMediaType"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/worksheets/status-counts": {
      "parameters": [
        {
          "$ref": "#/components/parameters/APIVersion"
        }
      ],
      "get": {
        "tags": [
          "Worksheets"
        ],
        "summary": "Count worksheets per lifecycle state",
        "x-summary-it": "Conta le schede per stato",
        "x-scope": "worksheets.read",
        "x-description-it": "Il numero di schede in ciascuno stato, con gli stessi filtri\ndell'elenco. Serve a popolare i contatori senza scaricare le schede.\n",
        "description": "Returns how many worksheets sit in each status, for the tab\ncounters above the mobile list. Accepts the same filters as\n`GET /v1/worksheets` minus `status` and pagination (`q`,\n`customer_id`, `from`/`to`/`preset` with the same date-filter\nsemantics), so the numbers always match what the\nlist shows. The applied range is echoed in `date_filter`.\n",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": false,
            "description": "Substring match against customer name.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "customer_id",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "from",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "to",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "$ref": "#/components/parameters/DatePreset"
          }
        ],
        "responses": {
          "200": {
            "description": "Counts per status.",
            "x-description-it": "Il numero di schede per stato.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WorksheetStatusCounts"
                },
                "example": {
                  "object": "worksheet_status_counts",
                  "draft": 3,
                  "waiting": 5,
                  "started": 2,
                  "completed": 8,
                  "delivered": 41,
                  "archived": 120,
                  "date_filter": {
                    "object": "date_filter",
                    "preset": "year",
                    "from": "2026-01-01",
                    "to": "2026-12-31"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/worksheets/{id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/APIVersion"
        },
        {
          "name": "id",
          "in": "path",
          "required": true,
          "description": "Worksheet id (UUID).",
          "schema": {
            "type": "string"
          }
        }
      ],
      "get": {
        "tags": [
          "Worksheets"
        ],
        "summary": "Retrieve a worksheet",
        "x-summary-it": "Recupera una scheda lavoro",
        "x-scope": "worksheets.read",
        "x-description-it": "Restituisce la scheda con le righe (`line_items`), i documenti\nallegati e i totali calcolati. `frozen` indica che la scheda ha già\ngenerato un documento fiscale e non è più modificabile;\n`linked_invoices` e `linked_receipt` puntano ai documenti emessi.\n",
        "description": "Returns a single worksheet (with embedded line items) by id,\nscoped to the authenticated user's company. Unknown ids, ids\nfrom another company, and soft-deleted rows all return 404 —\nnever 403.\n",
        "responses": {
          "200": {
            "description": "Worksheet found. Includes `line_items`.",
            "x-description-it": "La scheda lavoro richiesta.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Worksheet"
                },
                "example": {
                  "object": "worksheet",
                  "id": "a5d21e64-8f37-4b90-ae12-3c7d6b048f95",
                  "seq_number": 412,
                  "status": "started",
                  "title": "Tagliando 60.000 km",
                  "customer_id": "4c911f08-7713-4e83-bdf6-a0692a1b43fd",
                  "customer_name": "Mario Rossi",
                  "vehicle_id": "8821f0ac-5d2b-41e7-9a10-6c3f2b7d4e55",
                  "vehicle_name": "Fiat Panda",
                  "vehicle_license": "AB123CD",
                  "work_category_id": "3a8e5c21-9d76-4f03-b418-2c6a9e7d5b40",
                  "work_category_name": "Tagliando",
                  "km": 61240,
                  "total_before_tax": 233.9,
                  "total_tax": 51.46,
                  "total_after_tax": 285.36,
                  "total_discount": 0,
                  "total_payments": 150.0,
                  "outstanding_payments": 135.36,
                  "fiscal_document_type": "fattura",
                  "frozen": false,
                  "invoice_id": null,
                  "receipt_id": null,
                  "started_at": "2026-07-15T08:30:00Z",
                  "completed_at": null,
                  "delivered_at": null,
                  "created_at": "2026-07-15T08:12:00Z",
                  "updated_at": "2026-07-16T10:02:00Z",
                  "comments": "Sostituire anche il filtro abitacolo.",
                  "line_items": [
                    {
                      "object": "work_line_item",
                      "id": "7c0f6c4e-9b1a-4c2e-9a5d-2f3b1e8a4d10",
                      "article_id": null,
                      "description": "Olio motore 5W30",
                      "quantity": 4,
                      "unit_type": "pz",
                      "unit_price": 18.5,
                      "unit_discount_rate": 0,
                      "tax_rate": 22,
                      "vat_code": "22",
                      "total_before_tax": 74.0,
                      "total_tax": 16.28,
                      "total_after_tax": 90.28,
                      "created_at": "2026-07-15T08:31:00Z"
                    },
                    {
                      "object": "work_line_item",
                      "id": "0b4a2d6f-1c3e-4f5a-8b9c-7d6e5f4a3b2c",
                      "article_id": null,
                      "description": "Manodopera tagliando",
                      "quantity": 2,
                      "unit_type": "pz",
                      "unit_price": 79.95,
                      "unit_discount_rate": 0,
                      "tax_rate": 22,
                      "vat_code": "22",
                      "total_before_tax": 159.9,
                      "total_tax": 35.18,
                      "total_after_tax": 195.08,
                      "position": 1,
                      "created_at": "2026-07-15T08:31:00Z"
                    }
                  ],
                  "documents": []
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMediaType"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      },
      "patch": {
        "tags": [
          "Worksheets"
        ],
        "summary": "Update a worksheet",
        "x-summary-it": "Aggiorna una scheda lavoro",
        "x-scope": "worksheets.write",
        "x-description-it": "Aggiorna solo i campi presenti nel corpo: gli altri restano\ninvariati, e `null` svuota il campo. Una scheda `frozen` (già\nfatturata) rifiuta la modifica con `409`.\n",
        "description": "Partial update following the standard three-state PATCH\nsemantics (omitted = unchanged, `null` = clear, value = set).\n\nOnly top-level editable fields are exposed here. Use\n`POST /v1/worksheets/{id}/transitions` to change `status`.\nLine-item mutation is out of scope for v1 — a dedicated\nsub-resource will land in a later pass.\n\nFields intentionally NOT patchable: `id`, `company_id`,\n`created_by`, `seq_number`, `status`, `customer_id`,\n`vehicle_id`, all totals, all `*_at` timestamps,\n`line_items`, `documents`.\n\nForeign-key references in the body (`work_category_id`,\n`assigned_to`) must resolve to resources owned by the\nauthenticated user's company. Unreachable refs return 422\n`parameter_invalid` pointing at the offending field.\n\n**Frozen worksheets** — when the worksheet's linked invoice\nhas been issued, the worksheet is frozen: its totals are\nnow part of the invoice's accounting record and must not\ndesync. PATCH returns 409 `conflict`. Cancel the invoice\nwith a credit note before editing.\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WorksheetPatchRequest"
              },
              "example": {
                "title": "Tagliando 60.000 km — completato",
                "km": 61310,
                "internal_comments": null
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Worksheet updated. Returns the full post-update entity.",
            "x-description-it": "La scheda lavoro aggiornata.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Worksheet"
                },
                "example": {
                  "object": "worksheet",
                  "id": "a5d21e64-8f37-4b90-ae12-3c7d6b048f95",
                  "seq_number": 412,
                  "status": "started",
                  "title": "Tagliando 60.000 km",
                  "customer_id": "4c911f08-7713-4e83-bdf6-a0692a1b43fd",
                  "customer_name": "Mario Rossi",
                  "vehicle_id": "8821f0ac-5d2b-41e7-9a10-6c3f2b7d4e55",
                  "vehicle_name": "Fiat Panda",
                  "vehicle_license": "AB123CD",
                  "work_category_id": "3a8e5c21-9d76-4f03-b418-2c6a9e7d5b40",
                  "work_category_name": "Tagliando",
                  "km": 61240,
                  "total_before_tax": 233.9,
                  "total_tax": 51.46,
                  "total_after_tax": 285.36,
                  "total_discount": 0,
                  "total_payments": 150.0,
                  "outstanding_payments": 135.36,
                  "fiscal_document_type": "fattura",
                  "frozen": false,
                  "invoice_id": null,
                  "receipt_id": null,
                  "started_at": "2026-07-15T08:30:00Z",
                  "completed_at": null,
                  "delivered_at": null,
                  "created_at": "2026-07-15T08:12:00Z",
                  "updated_at": "2026-07-16T10:02:00Z",
                  "comments": "Sostituire anche il filtro abitacolo.",
                  "line_items": [
                    {
                      "object": "work_line_item",
                      "id": "7c0f6c4e-9b1a-4c2e-9a5d-2f3b1e8a4d10",
                      "article_id": null,
                      "description": "Olio motore 5W30",
                      "quantity": 4,
                      "unit_type": "pz",
                      "unit_price": 18.5,
                      "unit_discount_rate": 0,
                      "tax_rate": 22,
                      "vat_code": "22",
                      "total_before_tax": 74.0,
                      "total_tax": 16.28,
                      "total_after_tax": 90.28,
                      "created_at": "2026-07-15T08:31:00Z"
                    },
                    {
                      "object": "work_line_item",
                      "id": "0b4a2d6f-1c3e-4f5a-8b9c-7d6e5f4a3b2c",
                      "article_id": null,
                      "description": "Manodopera tagliando",
                      "quantity": 2,
                      "unit_type": "pz",
                      "unit_price": 79.95,
                      "unit_discount_rate": 0,
                      "tax_rate": 22,
                      "vat_code": "22",
                      "total_before_tax": 159.9,
                      "total_tax": 35.18,
                      "total_after_tax": 195.08,
                      "position": 1,
                      "created_at": "2026-07-15T08:31:00Z"
                    }
                  ],
                  "documents": []
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/MalformedRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMediaType"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      },
      "delete": {
        "tags": [
          "Worksheets"
        ],
        "summary": "Soft-delete a worksheet",
        "x-summary-it": "Elimina una scheda lavoro",
        "x-scope": "worksheets.write",
        "x-description-it": "Eliminazione logica. Una scheda che ha già generato una fattura o\nuno scontrino non può essere eliminata: la richiesta risponde `409`\nfinché il documento fiscale esiste.\n",
        "description": "Marks the worksheet as deleted by stamping `deleted_at` with\nthe server clock. The row stays in the database; subsequent\nGET/PATCH/DELETE all 404.\n\nReturns the deleted resource (200) rather than 204 No Content\nfor shape consistency with the rest of the API.\n\n**Frozen worksheets** — refuses to soft-delete when the\nworksheet's linked invoice has been issued (would orphan\nthe invoice's basis). Returns 409 `conflict`. Cancel the\ninvoice with a credit note first.\n",
        "responses": {
          "200": {
            "description": "Worksheet soft-deleted. `deleted_at` is populated.",
            "x-description-it": "Conferma di eliminazione.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Worksheet"
                },
                "example": {
                  "object": "worksheet",
                  "id": "a5d21e64-8f37-4b90-ae12-3c7d6b048f95",
                  "seq_number": 412,
                  "status": "started",
                  "title": "Tagliando 60.000 km",
                  "deleted_at": "2026-07-20T11:04:00Z"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/worksheets/{id}/checkins": {
      "parameters": [
        {
          "$ref": "#/components/parameters/APIVersion"
        },
        {
          "name": "id",
          "in": "path",
          "required": true,
          "description": "Worksheet id (UUID).",
          "schema": {
            "type": "string"
          }
        }
      ],
      "get": {
        "tags": [
          "Worksheets"
        ],
        "summary": "Accettazioni linked to a scheda lavoro",
        "x-summary-it": "Accettazioni collegate alla scheda",
        "x-scope": "checkins.read",
        "x-description-it": "Le accettazioni da cui la scheda è stata generata. Restituisce\naccettazioni, quindi richiede il permesso `checkins.read` e non\n`worksheets.read`.\n",
        "description": "Every check-in whose `worksheet_id` points at this scheda (set by\n`POST /v1/checkins/{id}/convert` and the web conversion). Newest\nfirst, without line items. Unpaginated (`has_more` always false).\n",
        "responses": {
          "200": {
            "description": "Linked accettazioni.",
            "x-description-it": "Le accettazioni collegate alla scheda.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "object",
                    "data",
                    "has_more",
                    "url"
                  ],
                  "properties": {
                    "object": {
                      "type": "string",
                      "const": "list"
                    },
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Checkin"
                      }
                    },
                    "has_more": {
                      "type": "boolean",
                      "const": false
                    },
                    "url": {
                      "type": "string"
                    }
                  }
                },
                "example": {
                  "object": "list",
                  "data": [
                    {
                      "object": "checkin",
                      "id": "1c7a9e04-5f83-4b26-8d10-6e2b4a9c7f31",
                      "status": "convertita",
                      "vehicle_id": "8821f0ac-5d2b-41e7-9a10-6c3f2b7d4e55",
                      "customer_id": "4c911f08-7713-4e83-bdf6-a0692a1b43fd",
                      "created_at": "2026-07-15T07:55:00Z"
                    }
                  ],
                  "has_more": false,
                  "url": "/v1/worksheets/a5d21e64-8f37-4b90-ae12-3c7d6b048f95/checkins"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/worksheets/{id}/documents": {
      "parameters": [
        {
          "$ref": "#/components/parameters/APIVersion"
        },
        {
          "name": "id",
          "in": "path",
          "required": true,
          "description": "Identifier of the target resource.",
          "schema": {
            "type": "string"
          }
        }
      ],
      "post": {
        "tags": [
          "Worksheets"
        ],
        "summary": "Create a worksheet document",
        "x-summary-it": "Allega un documento alla scheda",
        "x-scope": "worksheets.write",
        "x-description-it": "Carica un file fra i documenti della scheda. La richiesta è\n`multipart/form-data` con il file nel campo `file`, non JSON.\n",
        "operationId": "worksheetDocumentCreate",
        "description": "multipart/form-data:\n\nfile         required — the document bytes (max 25MB)\ndescription  optional — display label; defaults to the file name\n",
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "required": [
                  "file"
                ],
                "properties": {
                  "file": {
                    "type": "string",
                    "format": "binary"
                  },
                  "description": {
                    "type": "string",
                    "description": "Display label; defaults to the file name."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "success",
            "x-description-it": "Il documento allegato.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WorksheetDocument"
                },
                "example": {
                  "object": "worksheet_document",
                  "id": "6b3e9f10-2c84-4d57-91ab-5e8f0a2d7c63",
                  "name": "preventivo-firmato.pdf",
                  "content_type": "application/pdf",
                  "description": "Preventivo firmato",
                  "file_size_bytes": 92418,
                  "url": "/v1/worksheets/a5d21e64-8f37-4b90-ae12-3c7d6b048f95/documents/6b3e9f10-2c84-4d57-91ab-5e8f0a2d7c63/download",
                  "created_at": "2026-07-15T09:40:00Z"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/worksheets/{id}/documents/{documentID}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/APIVersion"
        },
        {
          "name": "id",
          "in": "path",
          "required": true,
          "description": "Identifier of the target resource.",
          "schema": {
            "type": "string"
          }
        },
        {
          "name": "documentID",
          "in": "path",
          "required": true,
          "description": "Identifier of the target resource.",
          "schema": {
            "type": "string"
          }
        }
      ],
      "delete": {
        "tags": [
          "Worksheets"
        ],
        "summary": "Delete worksheet document",
        "x-summary-it": "Elimina un documento della scheda",
        "x-scope": "worksheets.write",
        "x-description-it": "Rimuove definitivamente il file allegato.\n",
        "operationId": "worksheetDocumentDelete",
        "description": "WorksheetDocumentDelete handles\nDELETE /v1/worksheets/{id}/documents/{documentID}.\n",
        "responses": {
          "200": {
            "description": "success (the removed document)",
            "x-description-it": "Conferma di eliminazione.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WorksheetDocument"
                },
                "example": {
                  "object": "worksheet_document",
                  "id": "6b3e9f10-2c84-4d57-91ab-5e8f0a2d7c63",
                  "name": "preventivo-firmato.pdf",
                  "content_type": "application/pdf",
                  "file_size_bytes": 92418,
                  "created_at": "2026-07-15T09:40:00Z"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/worksheets/{id}/documents/{documentID}/download": {
      "parameters": [
        {
          "$ref": "#/components/parameters/APIVersion"
        },
        {
          "name": "id",
          "in": "path",
          "required": true,
          "description": "Identifier of the target resource.",
          "schema": {
            "type": "string"
          }
        },
        {
          "name": "documentID",
          "in": "path",
          "required": true,
          "description": "Identifier of the target resource.",
          "schema": {
            "type": "string"
          }
        }
      ],
      "get": {
        "tags": [
          "Worksheets"
        ],
        "summary": "Download worksheet document",
        "x-summary-it": "Scarica un documento della scheda",
        "x-scope": "worksheets.read",
        "x-description-it": "Restituisce il contenuto del file, non JSON. È il percorso indicato\ndal campo `url` di ogni documento.\n",
        "operationId": "worksheetDocumentDownload",
        "description": "WorksheetDocumentDownload handles\nGET /v1/worksheets/{id}/documents/{documentID}/download.\n",
        "responses": {
          "200": {
            "description": "success",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/worksheets/{id}/line-items/batch": {
      "parameters": [
        {
          "$ref": "#/components/parameters/APIVersion"
        },
        {
          "name": "id",
          "in": "path",
          "required": true,
          "description": "Worksheet id (UUID).",
          "schema": {
            "type": "string"
          }
        }
      ],
      "post": {
        "tags": [
          "Worksheets"
        ],
        "summary": "Mutate worksheet line items in a batch",
        "x-summary-it": "Modifica le righe in blocco",
        "x-scope": "worksheets.write",
        "x-description-it": "Aggiunge, aggiorna, rimuove e riordina le righe in una sola\nrichiesta, applicata come singola transazione: se una operazione\nfallisce non viene applicata nessuna. I totali della scheda vengono\nricalcolati e restituiti. Collega una riga al magazzino valorizzando\n`article_id`; lasciandolo `null` la riga resta libera.\n",
        "description": "Adds, updates, removes, and reorders the worksheet's line items\nin a single atomic diff, then returns the full worksheet with\nrecomputed totals and the updated `line_items` array. The\ntop-level worksheet PATCH is metadata-only; all line-item\nmutation flows through this endpoint.\n\n**Operations** (all arrays optional; empty body is a no-op):\n`add` creates new lines, `update` replaces an existing line's\nfields by id, `remove` deletes lines by id, `reorder` sets the\nposition of existing untouched lines. An id may appear in at\nmost one of `remove`/`update`/`reorder`.\n\n**Update is remove+add internally** so article inventory stock\nreconciles through the normal path; an updated line therefore\nreceives a NEW id in the response — re-read ids from the\nreturned `line_items` rather than assuming stability.\n\n**`tax_rate` is server-resolved** from each line's `vat_code`;\nclients never send rates or totals.\n\n**Frozen worksheets** — blocked when the worksheet's linked\ninvoice has been issued (mutating lines would desync invoice\ntotals). Returns 409 `conflict`.\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/LineItemsBatchRequest"
              },
              "example": {
                "add": [
                  {
                    "description": "Brake pads (front)",
                    "quantity": 1,
                    "unit_type": "kit",
                    "unit_price": 89.9,
                    "unit_discount_rate": 0,
                    "vat_code": "22",
                    "article_id": null
                  }
                ],
                "update": [
                  {
                    "id": "7c0f6c4e-9b1a-4c2e-9a5d-2f3b1e8a4d10",
                    "description": "Labour — brake service",
                    "quantity": 1.5,
                    "unit_type": "pz",
                    "unit_price": 40,
                    "unit_discount_rate": 10,
                    "vat_code": "22"
                  }
                ],
                "remove": [
                  "0b4a2d6f-1c3e-4f5a-8b9c-7d6e5f4a3b2c"
                ],
                "reorder": [
                  {
                    "id": "9d8c7b6a-5f4e-3d2c-1b0a-9e8d7c6b5a40",
                    "position": 1
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Batch applied. Returns the updated worksheet (with `line_items`).",
            "x-description-it": "La scheda con le righe aggiornate e i totali ricalcolati.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Worksheet"
                },
                "example": {
                  "object": "worksheet",
                  "id": "a5d21e64-8f37-4b90-ae12-3c7d6b048f95",
                  "seq_number": 412,
                  "status": "started",
                  "title": "Tagliando 60.000 km",
                  "customer_id": "4c911f08-7713-4e83-bdf6-a0692a1b43fd",
                  "customer_name": "Mario Rossi",
                  "vehicle_id": "8821f0ac-5d2b-41e7-9a10-6c3f2b7d4e55",
                  "vehicle_name": "Fiat Panda",
                  "vehicle_license": "AB123CD",
                  "work_category_id": "3a8e5c21-9d76-4f03-b418-2c6a9e7d5b40",
                  "work_category_name": "Tagliando",
                  "km": 61240,
                  "total_before_tax": 233.9,
                  "total_tax": 51.46,
                  "total_after_tax": 285.36,
                  "total_discount": 0,
                  "total_payments": 150.0,
                  "outstanding_payments": 135.36,
                  "fiscal_document_type": "fattura",
                  "frozen": false,
                  "invoice_id": null,
                  "receipt_id": null,
                  "started_at": "2026-07-15T08:30:00Z",
                  "completed_at": null,
                  "delivered_at": null,
                  "created_at": "2026-07-15T08:12:00Z",
                  "updated_at": "2026-07-16T10:02:00Z",
                  "comments": "Sostituire anche il filtro abitacolo.",
                  "line_items": [
                    {
                      "object": "work_line_item",
                      "id": "7c0f6c4e-9b1a-4c2e-9a5d-2f3b1e8a4d10",
                      "article_id": null,
                      "description": "Olio motore 5W30",
                      "quantity": 4,
                      "unit_type": "pz",
                      "unit_price": 18.5,
                      "unit_discount_rate": 0,
                      "tax_rate": 22,
                      "vat_code": "22",
                      "total_before_tax": 74.0,
                      "total_tax": 16.28,
                      "total_after_tax": 90.28,
                      "created_at": "2026-07-15T08:31:00Z"
                    },
                    {
                      "object": "work_line_item",
                      "id": "0b4a2d6f-1c3e-4f5a-8b9c-7d6e5f4a3b2c",
                      "article_id": null,
                      "description": "Manodopera tagliando",
                      "quantity": 2,
                      "unit_type": "pz",
                      "unit_price": 79.95,
                      "unit_discount_rate": 0,
                      "tax_rate": 22,
                      "vat_code": "22",
                      "total_before_tax": 159.9,
                      "total_tax": 35.18,
                      "total_after_tax": 195.08,
                      "position": 1,
                      "created_at": "2026-07-15T08:31:00Z"
                    }
                  ],
                  "documents": []
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/MalformedRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMediaType"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/worksheets/{id}/payments": {
      "parameters": [
        {
          "$ref": "#/components/parameters/APIVersion"
        },
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        }
      ],
      "get": {
        "tags": [
          "Worksheets"
        ],
        "summary": "List a worksheet's payments",
        "x-summary-it": "Elenca i pagamenti della scheda",
        "x-scope": "worksheets.read",
        "x-description-it": "Gli incassi registrati sulla scheda. Il residuo da incassare è\n`outstanding_payments` sulla scheda stessa.\n",
        "description": "Payments registered against the worksheet (acconti/saldi).\nThey physically hang off the worksheet's invoice, but the mobile\nAPI exposes them worksheet-scoped so clients never deal with the\ninvoice indirection. Unpaginated.\n",
        "responses": {
          "200": {
            "description": "Payments, oldest first.",
            "x-description-it": "I pagamenti della scheda.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "object",
                    "data",
                    "has_more",
                    "url"
                  ],
                  "properties": {
                    "object": {
                      "type": "string",
                      "const": "list"
                    },
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Payment"
                      }
                    },
                    "has_more": {
                      "type": "boolean",
                      "const": false
                    },
                    "url": {
                      "type": "string"
                    }
                  }
                },
                "example": {
                  "object": "list",
                  "data": [
                    {
                      "object": "payment",
                      "id": "d47c0b58-3e91-42a6-8f05-1b9d6a3e2c74",
                      "amount": 150.0,
                      "payment_method": "MP01",
                      "payment_date": "2026-07-16",
                      "created_at": "2026-07-16T10:02:00Z"
                    }
                  ],
                  "has_more": false,
                  "url": "/v1/worksheets/a5d21e64-8f37-4b90-ae12-3c7d6b048f95/payments"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      },
      "post": {
        "tags": [
          "Worksheets"
        ],
        "summary": "Register a payment on a worksheet",
        "x-summary-it": "Registra un pagamento",
        "x-scope": "worksheets.write",
        "x-description-it": "Registra un incasso sulla scheda. `payment_method` usa i codici\nministeriali (`MP01` contanti, `MP02` assegno, `MP05` bonifico,\n`MP08` carta). L'importo può essere parziale: registra più pagamenti\nfino a coprire il totale.\n",
        "description": "Mirrors the web worksheet flow: the payment attaches to the\nworksheet's invoice — a draft is created from the worksheet when\nnone exists yet — and the invoice paid-state is refreshed.\n`payment_method` falls back to the customer's preferred method,\nthen the company default, when missing or invalid.\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PaymentCreateRequest"
              },
              "example": {
                "amount": 150.0,
                "payment_method": "MP01",
                "payment_date": "2026-07-16"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Payment registered.",
            "x-description-it": "Il pagamento registrato.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Payment"
                },
                "example": {
                  "object": "payment",
                  "id": "d47c0b58-3e91-42a6-8f05-1b9d6a3e2c74",
                  "amount": 150.0,
                  "payment_method": "MP01",
                  "payment_date": "2026-07-16",
                  "created_at": "2026-07-16T10:02:00Z"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/MalformedRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMediaType"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/worksheets/{id}/payments/{paymentID}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/APIVersion"
        },
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        },
        {
          "name": "paymentID",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        }
      ],
      "delete": {
        "tags": [
          "Worksheets"
        ],
        "summary": "Remove a payment from a worksheet",
        "x-summary-it": "Elimina un pagamento",
        "x-scope": "worksheets.write",
        "x-description-it": "Rimuove l'incasso e riporta il residuo della scheda al valore\nprecedente.\n",
        "description": "Hard-deletes the payment (payments have no soft-delete) and\nrefreshes the invoice paid-state. The payment must belong to\nthe worksheet — otherwise 404.\n",
        "responses": {
          "200": {
            "description": "The removed payment.",
            "x-description-it": "Conferma di eliminazione.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Payment"
                },
                "example": {
                  "object": "payment",
                  "id": "d47c0b58-3e91-42a6-8f05-1b9d6a3e2c74",
                  "amount": 150.0,
                  "payment_method": "MP01",
                  "payment_date": "2026-07-16",
                  "created_at": "2026-07-16T10:02:00Z"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/worksheets/{id}/transitions": {
      "parameters": [
        {
          "$ref": "#/components/parameters/APIVersion"
        },
        {
          "name": "id",
          "in": "path",
          "required": true,
          "description": "Worksheet id (UUID).",
          "schema": {
            "type": "string"
          }
        }
      ],
      "post": {
        "tags": [
          "Worksheets"
        ],
        "summary": "Apply a status transition",
        "x-summary-it": "Cambia stato alla scheda",
        "x-scope": "worksheets.write",
        "x-description-it": "Applica una transizione di stato e registra la data corrispondente\n(`started_at`, `completed_at`, `delivered_at`, `archived_at`). È il\nmodo corretto di far avanzare una scheda: non impostare `status` via\nPATCH, perché non aggiornerebbe le date.\n",
        "description": "Moves the worksheet between lifecycle states. Modeled as an\nRPC rather than a PATCH on `status` so the lifecycle is\nvisible in the URL surface and the server retains exclusive\ncontrol over which `*_at` columns get stamped.\n\nSupported `action` values mirror the entity status constants:\n`draft`, `waiting`, `started`, `completed`, `delivered`,\n`archived`. Any other value is rejected with 422.\n\n**Frozen worksheets** — transitions are blocked when the\nworksheet's linked invoice has been issued. A status flip\non a frozen worksheet could trigger downstream side effects\n(e.g. moving back to draft would unwind the basis the\ninvoice was minted from). Returns 409 `conflict`.\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WorksheetTransitionRequest"
              },
              "example": {
                "action": "started"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Transition applied. Returns the post-transition entity.",
            "x-description-it": "La scheda dopo la transizione.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Worksheet"
                },
                "example": {
                  "object": "worksheet",
                  "id": "a5d21e64-8f37-4b90-ae12-3c7d6b048f95",
                  "seq_number": 412,
                  "status": "started",
                  "title": "Tagliando 60.000 km",
                  "customer_id": "4c911f08-7713-4e83-bdf6-a0692a1b43fd",
                  "customer_name": "Mario Rossi",
                  "vehicle_id": "8821f0ac-5d2b-41e7-9a10-6c3f2b7d4e55",
                  "vehicle_name": "Fiat Panda",
                  "vehicle_license": "AB123CD",
                  "work_category_id": "3a8e5c21-9d76-4f03-b418-2c6a9e7d5b40",
                  "work_category_name": "Tagliando",
                  "km": 61240,
                  "total_before_tax": 233.9,
                  "total_tax": 51.46,
                  "total_after_tax": 285.36,
                  "total_discount": 0,
                  "total_payments": 150.0,
                  "outstanding_payments": 135.36,
                  "fiscal_document_type": "fattura",
                  "frozen": false,
                  "invoice_id": null,
                  "receipt_id": null,
                  "started_at": "2026-07-15T08:30:00Z",
                  "completed_at": null,
                  "delivered_at": null,
                  "created_at": "2026-07-15T08:12:00Z",
                  "updated_at": "2026-07-16T10:02:00Z",
                  "comments": "Sostituire anche il filtro abitacolo.",
                  "line_items": [
                    {
                      "object": "work_line_item",
                      "id": "7c0f6c4e-9b1a-4c2e-9a5d-2f3b1e8a4d10",
                      "article_id": null,
                      "description": "Olio motore 5W30",
                      "quantity": 4,
                      "unit_type": "pz",
                      "unit_price": 18.5,
                      "unit_discount_rate": 0,
                      "tax_rate": 22,
                      "vat_code": "22",
                      "total_before_tax": 74.0,
                      "total_tax": 16.28,
                      "total_after_tax": 90.28,
                      "created_at": "2026-07-15T08:31:00Z"
                    },
                    {
                      "object": "work_line_item",
                      "id": "0b4a2d6f-1c3e-4f5a-8b9c-7d6e5f4a3b2c",
                      "article_id": null,
                      "description": "Manodopera tagliando",
                      "quantity": 2,
                      "unit_type": "pz",
                      "unit_price": 79.95,
                      "unit_discount_rate": 0,
                      "tax_rate": 22,
                      "vat_code": "22",
                      "total_before_tax": 159.9,
                      "total_tax": 35.18,
                      "total_after_tax": 195.08,
                      "position": 1,
                      "created_at": "2026-07-15T08:31:00Z"
                    }
                  ],
                  "documents": []
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/MalformedRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMediaType"
          },
          "422": {
            "$ref": "#/components/responses/ValidationError"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/whoami": {
      "parameters": [
        {
          "$ref": "#/components/parameters/APIVersion"
        }
      ],
      "get": {
        "tags": [
          "Authentication"
        ],
        "summary": "Describe the calling API key",
        "x-summary-it": "Verifica la chiave API",
        "x-description-it": "Restituisce la chiave che sta effettuando la richiesta: nome,\nambiente, permessi concessi e azienda a cui appartiene.\n\nÈ l'unico endpoint senza permesso richiesto: qualunque chiave valida\npuò chiamarlo, ed è il modo più rapido per verificare che una chiave\nfunzioni. Un `200` significa che la chiave è valida e che il piano\nattivo include l'accesso API; `401` che la chiave è sconosciuta,\nrevocata o scaduta; `402` che il piano non include l'accesso API.\n",
        "operationId": "whoAmI",
        "description": "Returns the key making the request: its name, environment and the\nscopes it was granted, plus the company it authorises.\n\nDeliberately unscoped — every integrator's first call, and the\ncheapest answer to \"is my key working\". It reveals nothing the\ncaller does not already hold.\n\nA 200 means the key is valid AND the company's plan includes API\naccess. 401 means the key is unknown, revoked or expired; 402\nmeans the plan does not include API access.\n",
        "responses": {
          "200": {
            "description": "The calling key.",
            "x-description-it": "La chiave che sta effettuando la richiesta.",
            "headers": {
              "X-API-Version": {
                "$ref": "#/components/headers/APIVersion"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WhoAmI"
                },
                "example": {
                  "object": "api_key",
                  "id": "5f1a8c36-2e74-4b90-8d25-6c3f9a1e7b48",
                  "name": "Gestionale contabilità",
                  "environment": "live",
                  "scopes": [
                    "customers.read",
                    "customers.write",
                    "invoices.read"
                  ],
                  "company": {
                    "object": "company",
                    "id": "1f8c4e73-2a95-4b06-9d31-7e5a2c8b6f40",
                    "name": "Officina Rossi S.r.l."
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "PublicAPIKey": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "APIKey",
        "description": "Long-lived API key minted from Impostazioni → Accesso API.\nFormat `ofk_live_\u003c43 chars\u003e` (`ofk_test_` outside production).\nSend it as `Authorization: Bearer ofk_live_…`.\n\nA key authorises a COMPANY and carries per-resource scopes;\na call outside its scopes answers 403 `insufficient_scope`.\nKeys are shown once at creation and stored only as a digest.\n"
      }
    },
    "parameters": {
      "APIVersion": {
        "name": "X-API-Version",
        "in": "header",
        "required": true,
        "description": "The client's declared revision: the dated `/v1` contract this\nclient build was developed against. Floor-checked, not an\nallowlist — must be a `YYYY-MM-DD` date and at least the minimum\nsupported revision (`api_version_retired` below it, meaning the\nclient build is too old); dates newer than the deployed server\nare accepted and served with current behaviour. Clients should\nalso self-identify via the optional headers `X-Client`\n(`officina-mobile/1.0.0 (ios)`), `X-App-Version` (`1.0.0`),\n`X-Platform` (`ios`) and `X-OS-Version` (`18.5`) — all stamped\non the request logs (`client`, `app_version`, `platform`,\n`os_version`) so revision adoption and platform-specific issues\nare measurable before the floor is raised.\nCurrent: `2026-07-15`. Minimum: `2026-07-15` (single pre-launch\nrevision). See the README registry and\n`apihttp.CurrentAPIVersion` / `MinSupportedAPIVersion` for the\nsource of truth.\n",
        "schema": {
          "type": "string",
          "pattern": "^\\d{4}-\\d{2}-\\d{2}$",
          "example": "2026-07-15"
        }
      },
      "DatePreset": {
        "name": "preset",
        "in": "query",
        "required": false,
        "description": "Date filter preset. Relative presets (`rel30`/`rel90`/`rel365`)\nare resolved server-side from \"now\" and ignore `from`/`to`.\nAbsolute presets (`year`, `Q1`-`Q4`, `month`, `custom`) require\nboth `from` and `to`. `all` returns everything (no time bound).\nUnknown values are rejected with 422.\n\nOmitting `from`/`to`/`preset` gives the default window, the last\n12 months. This tier is stateless: a selection applies to the\nrequest that carries it and is never stored, so the same request\nalways covers the same range. The effective range is echoed back\nin `date_filter`.\n",
        "schema": {
          "type": "string",
          "enum": [
            "rel30",
            "rel90",
            "rel365",
            "year",
            "Q1",
            "Q2",
            "Q3",
            "Q4",
            "month",
            "custom",
            "all"
          ]
        }
      }
    },
    "headers": {
      "APIVersion": {
        "description": "Echoes the `X-API-Version` value the server processed.",
        "schema": {
          "type": "string"
        }
      }
    },
    "schemas": {
      "WhoAmI": {
        "type": "object",
        "description": "The API key making the request, as returned by GET /v1/whoami.\n",
        "required": [
          "object",
          "id",
          "name",
          "environment",
          "scopes",
          "company"
        ],
        "properties": {
          "object": {
            "type": "string",
            "description": "always \"api_key\""
          },
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string",
            "description": "The label the workshop gave the key."
          },
          "environment": {
            "type": "string",
            "enum": [
              "live",
              "test"
            ],
            "description": "Matches the token prefix (ofk_live_ / ofk_test_)."
          },
          "scopes": {
            "type": "array",
            "description": "Granted scopes, e.g. \"customers.read\".",
            "items": {
              "type": "string"
            }
          },
          "company": {
            "type": "object",
            "required": [
              "object",
              "id",
              "name"
            ],
            "properties": {
              "object": {
                "type": "string",
                "description": "always \"company\""
              },
              "id": {
                "type": "string"
              },
              "name": {
                "type": "string"
              }
            }
          }
        }
      },
      "LoginMethodRequest": {
        "type": "object",
        "required": [
          "email"
        ],
        "properties": {
          "email": {
            "type": "string",
            "format": "email",
            "description": "Email to resolve the credential step for."
          }
        }
      },
      "LoginRequest": {
        "type": "object",
        "required": [
          "email",
          "password"
        ],
        "properties": {
          "email": {
            "type": "string",
            "format": "email"
          },
          "password": {
            "type": "string",
            "format": "password",
            "minLength": 1
          }
        }
      },
      "SignOnTokenRequest": {
        "type": "object",
        "required": [
          "email"
        ],
        "properties": {
          "email": {
            "type": "string",
            "format": "email"
          }
        }
      },
      "LoginWithTokenRequest": {
        "type": "object",
        "required": [
          "email",
          "sign_on_token"
        ],
        "properties": {
          "email": {
            "type": "string",
            "format": "email"
          },
          "sign_on_token": {
            "type": "string",
            "description": "One-time code received via email.",
            "minLength": 1
          }
        }
      },
      "Profile": {
        "type": "object",
        "required": [
          "object",
          "id",
          "first_name",
          "last_name",
          "email",
          "phone",
          "phone_national",
          "role",
          "permissions",
          "all_permissions",
          "modules"
        ],
        "description": "The authenticated user's own account (Profilo personale).\nEmail is read-only — the login email cannot be changed from\nthis surface, matching the web.\n\n`permissions` + `modules` are the app's gating surface: the\nuser's granted permission keys and the company's feature\nflags, resolved server-side with the same semantics the web\nsidebar (dashboard-sidebar.html) reads them. The client hides\nnavigation/actions from them; the API remains the enforcement\nlayer.\n",
        "properties": {
          "object": {
            "type": "string",
            "const": "profile"
          },
          "id": {
            "type": "string"
          },
          "first_name": {
            "type": "string"
          },
          "last_name": {
            "type": "string"
          },
          "email": {
            "type": "string",
            "format": "email"
          },
          "phone": {
            "type": [
              "string",
              "null"
            ],
            "description": "Stored E.164 value."
          },
          "phone_national": {
            "type": [
              "string",
              "null"
            ],
            "description": "phone minus the +39 dial code, for the fixed-prefix input."
          },
          "role": {
            "type": [
              "string",
              "null"
            ]
          },
          "permissions": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Sorted permission keys, e.g. \"invoices.read\"."
          },
          "all_permissions": {
            "type": "boolean",
            "description": "True when the user holds every permission in the system\n(owner-equivalent). Gates owner-only surfaces like the AI\nassistant — exposed because the client cannot know the\nfull permission catalog.\n"
          },
          "modules": {
            "type": "object",
            "required": [
              "checkins",
              "electronic_invoices",
              "expenses",
              "dealership",
              "vehicle_rental",
              "tyre_storage",
              "service_catalog",
              "inventory",
              "technical_data",
              "license_plate_search",
              "public_profile",
              "ai_assistant",
              "has_worksheets"
            ],
            "description": "Company feature flags. `technical_data` already folds in\nthe subscription check (Company.CanUseTechnicalData);\n`has_worksheets` backs the web's \"Situazione appears\nafter the first worksheet\" rule.\n",
            "properties": {
              "checkins": {
                "type": "boolean"
              },
              "electronic_invoices": {
                "type": "boolean"
              },
              "expenses": {
                "type": "boolean"
              },
              "dealership": {
                "type": "boolean"
              },
              "vehicle_rental": {
                "type": "boolean"
              },
              "tyre_storage": {
                "type": "boolean"
              },
              "service_catalog": {
                "type": "boolean"
              },
              "inventory": {
                "type": "boolean"
              },
              "technical_data": {
                "type": "boolean"
              },
              "license_plate_search": {
                "type": "boolean"
              },
              "public_profile": {
                "type": "boolean"
              },
              "ai_assistant": {
                "type": "boolean"
              },
              "has_worksheets": {
                "type": "boolean"
              }
            }
          }
        }
      },
      "WritableTimestamp": {
        "type": "string",
        "format": "date-time",
        "description": "RFC 3339 timestamp supplied by the client. Must fall between\n1900-01-01 and 2100-12-31. A year outside that window is a\ntypo (2025 mistyped as 0025 is the common one), and the app's\nown date pickers refuse the same range — a record written with\none would not be editable in the web UI afterwards. Out of\nrange → 422 `parameter_invalid` with `param` pointing at the\noffending field.\n"
      },
      "WritableTimestampNullable": {
        "type": [
          "string",
          "null"
        ],
        "format": "date-time",
        "description": "Nullable client-supplied RFC 3339 timestamp. Same\n1900-01-01 … 2100-12-31 window as `WritableTimestamp`;\n`null` clears the field.\n"
      },
      "CustomerCreateRequest": {
        "type": "object",
        "required": [
          "customer_type"
        ],
        "description": "Body of `POST /v1/customers`. `company_id` and `created_by`\nare always derived from the authenticated bearer token and\nMUST NOT appear in the body.\n\nRequired: `customer_type`. Everything else is optional;\nomitted keys map to NULL on the entity. There is no\nthree-state semantic on create — there's nothing to \"leave\nuntouched\".\n\nFields not listed here are intentionally NOT settable on\ncreate: `id`, `company_id`, `created_by`, system timestamps,\nand all computed display fields.\n",
        "properties": {
          "customer_type": {
            "type": "string"
          },
          "full_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "email": {
            "type": [
              "string",
              "null"
            ],
            "format": "email"
          },
          "phone": {
            "type": [
              "string",
              "null"
            ],
            "description": "National number as typed (e.g. \"333 1234567\"). Combined\nwith `phone_dial_code` and validated server-side via the\nweb customer-form rules (\"+39\" allows Italian mobiles and\nlandlines; other known dial codes get a basic E.164 check);\nstored and returned as one E.164 string. Invalid pairs are\nrejected with 422 `parameter_invalid`.\n"
          },
          "phone_dial_code": {
            "type": [
              "string",
              "null"
            ],
            "description": "E.164 dial code (default \"+39\"). Only read alongside `phone`."
          },
          "address": {
            "type": [
              "string",
              "null"
            ]
          },
          "city": {
            "type": [
              "string",
              "null"
            ]
          },
          "zip_code": {
            "type": [
              "string",
              "null"
            ]
          },
          "province": {
            "type": [
              "string",
              "null"
            ]
          },
          "tax_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "fiscal_code": {
            "type": [
              "string",
              "null"
            ]
          },
          "pec": {
            "type": [
              "string",
              "null"
            ]
          },
          "destination_code": {
            "type": [
              "string",
              "null"
            ]
          },
          "payment_method": {
            "type": [
              "string",
              "null"
            ]
          },
          "comments": {
            "type": [
              "string",
              "null"
            ]
          },
          "birthday": {
            "$ref": "#/components/schemas/WritableTimestampNullable"
          },
          "price_list_id": {
            "type": [
              "string",
              "null"
            ]
          }
        },
        "additionalProperties": false
      },
      "CustomerPatchRequest": {
        "type": "object",
        "description": "Body of `PATCH /v1/customers/{id}`. Every field is optional on\nthe wire and carries three-state semantics:\n\n- Field omitted → leave the entity field untouched.\n- Field present with value `null` → clear the entity field.\n- Field present with a value → set the entity field.\n\nFields not listed here are intentionally NOT patchable through\nthe API: `id`, `company_id`, `created_by`, system timestamps\n(`created_at`, `updated_at`, `deleted_at`), and computed/derived\nfields (`total_spent`, `next_appointment`, `last_vehicle_*`).\nDocuments are managed via the documents sub-resource.\n\n`customer_type` is NOT NULL at the entity layer; sending it as\nexplicit `null` is rejected with 422 `parameter_invalid`.\n",
        "properties": {
          "customer_type": {
            "type": "string"
          },
          "full_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "email": {
            "type": [
              "string",
              "null"
            ],
            "format": "email"
          },
          "phone": {
            "type": [
              "string",
              "null"
            ],
            "description": "National number as typed (e.g. \"333 1234567\"). Combined\nwith `phone_dial_code` and validated server-side via the\nweb customer-form rules (\"+39\" allows Italian mobiles and\nlandlines; other known dial codes get a basic E.164 check);\nstored and returned as one E.164 string. Invalid pairs are\nrejected with 422 `parameter_invalid`.\n"
          },
          "phone_dial_code": {
            "type": [
              "string",
              "null"
            ],
            "description": "E.164 dial code (default \"+39\"). Only read alongside `phone`."
          },
          "address": {
            "type": [
              "string",
              "null"
            ]
          },
          "city": {
            "type": [
              "string",
              "null"
            ]
          },
          "zip_code": {
            "type": [
              "string",
              "null"
            ]
          },
          "province": {
            "type": [
              "string",
              "null"
            ]
          },
          "tax_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "fiscal_code": {
            "type": [
              "string",
              "null"
            ]
          },
          "pec": {
            "type": [
              "string",
              "null"
            ]
          },
          "destination_code": {
            "type": [
              "string",
              "null"
            ]
          },
          "payment_method": {
            "type": [
              "string",
              "null"
            ]
          },
          "comments": {
            "type": [
              "string",
              "null"
            ]
          },
          "birthday": {
            "$ref": "#/components/schemas/WritableTimestampNullable"
          },
          "price_list_id": {
            "type": [
              "string",
              "null"
            ]
          }
        },
        "additionalProperties": false
      },
      "VehicleDocument": {
        "type": "object",
        "required": [
          "object",
          "id",
          "name",
          "content_type",
          "description",
          "url",
          "file_size_bytes",
          "created_at"
        ],
        "description": "One attachment on a vehicle (Documenti Allegati). `url` is the\nauthenticated download route — fetch it with the bearer and\nhand the bytes to the OS viewer/share sheet.\n",
        "properties": {
          "object": {
            "type": "string",
            "const": "vehicle_document"
          },
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string",
            "description": "Original file name."
          },
          "content_type": {
            "type": "string"
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "url": {
            "type": "string",
            "example": "/v1/vehicles/8821f0ac-5d2b-41e7-9a10-6c3f2b7d4e55/documents/3b91ee0c-19b4-4f0a-9c2e-7d51a0c88e14/download"
          },
          "file_size_bytes": {
            "type": "integer"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "VehicleCreateRequest": {
        "type": "object",
        "required": [
          "manufacturer_id",
          "model"
        ],
        "description": "Body of `POST /v1/vehicles`. `company_id` and `created_by`\nare always derived from the authenticated bearer token and\nMUST NOT appear in the body. Rental fields (`is_rental`,\n`rental_*`) are also not accepted — rental setup is a\nseparate flow.\n",
        "properties": {
          "manufacturer_id": {
            "type": "string"
          },
          "model": {
            "type": "string"
          },
          "license": {
            "type": [
              "string",
              "null"
            ]
          },
          "variant": {
            "type": [
              "string",
              "null"
            ]
          },
          "vin": {
            "type": [
              "string",
              "null"
            ]
          },
          "transmission": {
            "type": [
              "string",
              "null"
            ]
          },
          "engine_power_kw": {
            "type": [
              "integer",
              "null"
            ]
          },
          "engine_displacement_ccm": {
            "type": [
              "integer",
              "null"
            ]
          },
          "vehicle_weight_kg": {
            "type": [
              "integer",
              "null"
            ]
          },
          "engine_category": {
            "type": [
              "string",
              "null"
            ]
          },
          "registered_at": {
            "$ref": "#/components/schemas/WritableTimestampNullable"
          },
          "comments": {
            "type": [
              "string",
              "null"
            ]
          },
          "customer_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "km": {
            "type": [
              "number",
              "null"
            ]
          },
          "km_registered_at": {
            "$ref": "#/components/schemas/WritableTimestampNullable"
          },
          "last_inspection_at": {
            "$ref": "#/components/schemas/WritableTimestampNullable"
          },
          "next_inspection_at": {
            "$ref": "#/components/schemas/WritableTimestampNullable"
          },
          "custom_fields": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": true,
            "description": "Free-form key/value blob. The API treats this as opaque\nand stores it as a single JSON column.\n"
          }
        },
        "additionalProperties": false
      },
      "VehiclePatchRequest": {
        "type": "object",
        "description": "Body of `PATCH /v1/vehicles/{id}`. Every field is optional on\nthe wire and carries three-state semantics:\n\n- Field omitted → leave the entity field untouched.\n- Field present with value `null` → clear the entity field.\n- Field present with a value → set the entity field.\n\nFields not listed here are intentionally NOT patchable:\n`id`, `company_id`, `created_by`, system timestamps\n(`created_at`, `updated_at`, `deleted_at`), rental fields\n(`is_rental`, `rental_*`), and computed/joined fields\n(`next_appointment`, `manufacturer_name`, `customer_name`).\n\n`model` and `manufacturer_id` are NOT NULL at the entity\nlayer; sending either as explicit `null` is rejected with\n422 `parameter_invalid`.\n\n`custom_fields` is replaced wholesale — per-key merging is\nnot supported.\n",
        "properties": {
          "manufacturer_id": {
            "type": "string"
          },
          "model": {
            "type": "string"
          },
          "license": {
            "type": [
              "string",
              "null"
            ]
          },
          "variant": {
            "type": [
              "string",
              "null"
            ]
          },
          "vin": {
            "type": [
              "string",
              "null"
            ]
          },
          "transmission": {
            "type": [
              "string",
              "null"
            ]
          },
          "engine_power_kw": {
            "type": [
              "integer",
              "null"
            ]
          },
          "engine_displacement_ccm": {
            "type": [
              "integer",
              "null"
            ]
          },
          "vehicle_weight_kg": {
            "type": [
              "integer",
              "null"
            ]
          },
          "engine_category": {
            "type": [
              "string",
              "null"
            ]
          },
          "registered_at": {
            "$ref": "#/components/schemas/WritableTimestampNullable"
          },
          "comments": {
            "type": [
              "string",
              "null"
            ]
          },
          "customer_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "km": {
            "type": [
              "number",
              "null"
            ]
          },
          "km_registered_at": {
            "$ref": "#/components/schemas/WritableTimestampNullable"
          },
          "last_inspection_at": {
            "$ref": "#/components/schemas/WritableTimestampNullable"
          },
          "next_inspection_at": {
            "$ref": "#/components/schemas/WritableTimestampNullable"
          },
          "custom_fields": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": true
          }
        },
        "additionalProperties": false
      },
      "WorksheetCreateRequest": {
        "type": "object",
        "required": [
          "customer_id"
        ],
        "description": "Body of `POST /v1/worksheets`. Only client-supplied fields\nappear here — server-managed columns (id, company_id,\ncreated_by, seq_number, status, all timestamps, totals)\nare filled in automatically.\n\nLine items are NOT accepted on creation; a created worksheet\nstarts with an empty `line_items` array.\n",
        "properties": {
          "customer_id": {
            "type": "string"
          },
          "vehicle_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "title": {
            "type": [
              "string",
              "null"
            ]
          },
          "comments": {
            "type": [
              "string",
              "null"
            ]
          },
          "internal_comments": {
            "type": [
              "string",
              "null"
            ]
          },
          "assigned_to": {
            "type": [
              "string",
              "null"
            ]
          },
          "work_category_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "km": {
            "type": [
              "number",
              "null"
            ]
          }
        },
        "additionalProperties": false
      },
      "WorksheetPatchRequest": {
        "type": "object",
        "description": "Body of `PATCH /v1/worksheets/{id}`. Every field carries\nthree-state semantics:\n\n- Field omitted → leave the entity field untouched.\n- Field present with value `null` → clear the entity field.\n- Field present with a value → set the entity field.\n\nFields not listed here are intentionally NOT patchable:\n`id`, `company_id`, `created_by`, `seq_number`, `status`\n(use the transitions endpoint), `customer_id`, `vehicle_id`,\nall totals, all `*_at` timestamps, `line_items`, `documents`.\n",
        "properties": {
          "title": {
            "type": [
              "string",
              "null"
            ]
          },
          "comments": {
            "type": [
              "string",
              "null"
            ]
          },
          "internal_comments": {
            "type": [
              "string",
              "null"
            ]
          },
          "assigned_to": {
            "type": [
              "string",
              "null"
            ]
          },
          "work_category_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "km": {
            "type": [
              "number",
              "null"
            ]
          }
        },
        "additionalProperties": false
      },
      "WorksheetTransitionRequest": {
        "type": "object",
        "required": [
          "action"
        ],
        "description": "Body of `POST /v1/worksheets/{id}/transitions`. The server\nruns the matching state transition and stamps the\ncorresponding `*_at` column.\n",
        "properties": {
          "action": {
            "type": "string",
            "enum": [
              "draft",
              "waiting",
              "started",
              "completed",
              "delivered",
              "archived"
            ]
          }
        },
        "additionalProperties": false
      },
      "LineItemInput": {
        "type": "object",
        "required": [
          "description",
          "quantity",
          "unit_type",
          "unit_price",
          "vat_code"
        ],
        "description": "A line item supplied by the client in an `add` or `update`\noperation. `tax_rate` is NOT accepted — it is resolved\nserver-side from `vat_code` so the stored rate always matches\nthe company VAT table. Money/quantity fields are in the working\ncurrency unit and natural units respectively.\n",
        "properties": {
          "description": {
            "type": "string",
            "description": "Required, non-empty."
          },
          "quantity": {
            "type": "number",
            "description": "Required, \u003e 0."
          },
          "unit_type": {
            "type": "string",
            "description": "Required (e.g. \"pz\", \"kg\", \"L\", \"kit\")."
          },
          "unit_price": {
            "type": "number",
            "description": "Required, \u003e= 0."
          },
          "unit_discount_rate": {
            "type": "number",
            "description": "Percent, 0..100. Defaults to 0."
          },
          "vat_code": {
            "type": "string",
            "description": "Required; must exist in the company VAT table."
          },
          "article_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "Optional link to a catalog article. When set it must\nreference an article owned by the caller's company. On\nworksheets, article-backed lines move inventory stock.\n"
          },
          "position": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Optional ordering position. When omitted the item is\nappended after the current maximum position.\n"
          }
        },
        "additionalProperties": false
      },
      "LineItemUpdate": {
        "description": "Identifies an existing line item by `id` and carries its full\nreplacement fields (a full replace of editable fields, not a\npartial patch). Applied as a remove-then-add internally so\narticle stock reconciles through the normal path; as a result\nthe updated line receives a NEW id in the response.\n",
        "allOf": [
          {
            "$ref": "#/components/schemas/LineItemInput"
          },
          {
            "type": "object",
            "required": [
              "id"
            ],
            "properties": {
              "id": {
                "type": "string"
              }
            }
          }
        ]
      },
      "LineItemReorder": {
        "type": "object",
        "required": [
          "id",
          "position"
        ],
        "description": "Sets the position of an existing line item that is not otherwise\nadded, updated, or removed in the same batch.\n",
        "properties": {
          "id": {
            "type": "string"
          },
          "position": {
            "type": "integer"
          }
        },
        "additionalProperties": false
      },
      "LineItemsBatchRequest": {
        "type": "object",
        "description": "Body of `POST /v1/worksheets/{id}/line-items/batch` and\n`POST /v1/quotes/{id}/line-items/batch`. Applies a single diff\nagainst the resource's current line-item set; totals are\nrecomputed server-side from the result and the full resource is\nreturned.\n\nAll four arrays are optional; an empty body is a no-op. An id may\nappear in at most one of `remove`/`update`/`reorder` — listing it\ntwice is a 422. Ids in `remove`/`update`/`reorder` must belong to\nthe target resource.\n",
        "properties": {
          "add": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/LineItemInput"
            }
          },
          "update": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/LineItemUpdate"
            }
          },
          "remove": {
            "type": "array",
            "description": "Ids of line items to delete.",
            "items": {
              "type": "string"
            }
          },
          "reorder": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/LineItemReorder"
            }
          }
        },
        "additionalProperties": false
      },
      "QuoteCreateRequest": {
        "type": "object",
        "required": [
          "customer_id"
        ],
        "description": "Body of `POST /v1/quotes`. Server-managed columns (id,\ncompany_id, created_by, seq_number, status, all timestamps,\ntotals) are filled in automatically. Line items are not\naccepted on creation.\n",
        "properties": {
          "customer_id": {
            "type": "string"
          },
          "vehicle_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "title": {
            "type": [
              "string",
              "null"
            ]
          },
          "comments": {
            "type": [
              "string",
              "null"
            ]
          },
          "internal_comments": {
            "type": [
              "string",
              "null"
            ]
          },
          "work_category_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "km": {
            "type": [
              "number",
              "null"
            ]
          }
        },
        "additionalProperties": false
      },
      "QuotePatchRequest": {
        "type": "object",
        "description": "Body of `PATCH /v1/quotes/{id}`. Three-state semantics\n(omitted = leave unchanged, explicit null = clear, concrete\nvalue = set). `status`, `seq_number`, `customer_id`,\n`vehicle_id`, totals, `worksheet_id`, and timestamps are\nNOT patchable.\n",
        "properties": {
          "title": {
            "type": [
              "string",
              "null"
            ]
          },
          "comments": {
            "type": [
              "string",
              "null"
            ]
          },
          "internal_comments": {
            "type": [
              "string",
              "null"
            ]
          },
          "work_category_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "km": {
            "type": [
              "number",
              "null"
            ]
          }
        },
        "additionalProperties": false
      },
      "QuoteTransitionRequest": {
        "type": "object",
        "required": [
          "action"
        ],
        "description": "Body of `POST /v1/quotes/{id}/transitions`. Unknown action\nvalues are rejected with 422 `parameter_invalid`.\n",
        "properties": {
          "action": {
            "type": "string",
            "enum": [
              "waiting",
              "accepted",
              "rejected"
            ]
          }
        },
        "additionalProperties": false
      },
      "InvoiceCreateRequest": {
        "type": "object",
        "required": [
          "worksheet_id",
          "payment_method"
        ],
        "description": "Body of `POST /v1/invoices`. A mobile invoice is always\ncreated from an existing worksheet — there is no \"blank\ninvoice + add lines\" flow on this tier.\n",
        "properties": {
          "worksheet_id": {
            "type": "string",
            "description": "Source worksheet UUID (must belong to caller's company)."
          },
          "payment_method": {
            "type": "string",
            "description": "Free-form payment method label.",
            "minLength": 1
          },
          "payment_due_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "Optional. RFC 3339 between 1900-01-01 and 2100-12-31\n(see `WritableTimestampNullable`). Defaults to today + the\ncompany's configured payment term.\n"
          }
        },
        "additionalProperties": false
      },
      "InvoicePatchRequest": {
        "type": "object",
        "description": "Body of `PATCH /v1/invoices/{id}`. Three-state per field:\nomitted (leave alone), `null` (clear, where nullable),\nconcrete value (set). Non-nullable columns reject `null`\nwith 422 `parameter_invalid`.\n\nFields not listed here are intentionally NOT patchable:\n`id`, `seq_number`, `status`, `customer_id`, `vehicle_id`,\n`worksheet_id`, all totals, all timestamps except\n`payment_due_at`. Line item add/remove is not exposed.\n\n`title`, `comments`, `internal_comments` are deliberately\nabsent today: the service-layer Update contract doesn't\ncarry these columns yet, so accepting them on the wire\nwould silently drop the write. They will be added once\nthe service is extended.\n",
        "properties": {
          "payment_method": {
            "type": "string"
          },
          "payment_condition": {
            "type": "string"
          },
          "payment_due_at": {
            "$ref": "#/components/schemas/WritableTimestamp"
          },
          "vat_exigibility": {
            "type": "string"
          },
          "sezionale_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "pa_document_type": {
            "type": [
              "string",
              "null"
            ]
          },
          "pa_document_number": {
            "type": [
              "string",
              "null"
            ]
          },
          "pa_document_date": {
            "$ref": "#/components/schemas/WritableTimestampNullable"
          },
          "pa_cig": {
            "type": [
              "string",
              "null"
            ]
          },
          "pa_cup": {
            "type": [
              "string",
              "null"
            ]
          }
        },
        "additionalProperties": false
      },
      "InvoiceIssueRequest": {
        "type": "object",
        "description": "Body of `POST /v1/invoices/{id}/issue`. All fields are\noptional; an empty body uses server defaults.\n",
        "properties": {
          "issued_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "Override the server clock. Must fall between 1900-01-01\nand 2100-12-31 (see `WritableTimestampNullable`).\nBack-dates that would violate the monotonic per-company\nsequence return 409.\n"
          }
        },
        "additionalProperties": false
      },
      "CreditNoteCreateRequest": {
        "type": "object",
        "required": [
          "invoice_id"
        ],
        "description": "Body of `POST /v1/credit-notes`. Credit notes are derivative\ndocuments — created from an existing invoice. The source\ninvoice MUST belong to the authenticated user's company.\n\nServer-derived / immutable fields are NOT accepted: `id`,\n`company_id` (from source invoice), `created_by` (from\nauth), `seq_number` (server-assigned), totals (copied from\ninvoice), timestamps.\n",
        "properties": {
          "invoice_id": {
            "type": "string",
            "description": "Source invoice UUID (must belong to caller's company)."
          },
          "title": {
            "type": [
              "string",
              "null"
            ]
          },
          "sezionale_id": {
            "type": [
              "string",
              "null"
            ]
          }
        },
        "additionalProperties": false
      },
      "CreditNotePatchRequest": {
        "type": "object",
        "description": "Body of `PATCH /v1/credit-notes/{id}`. `title` is the ONLY\nmutable field on a credit note. All other fields are derived\nfrom the source invoice, immutable identity, or\nserver-managed.\n\nThree-state for `title`:\n\n- Omitted → leave unchanged.\n- Explicit JSON `null` → clear (SQL NULL).\n- Concrete value → set.\n\nSending any other key returns 400 `malformed_request`.\n",
        "properties": {
          "title": {
            "type": [
              "string",
              "null"
            ]
          }
        },
        "additionalProperties": false
      },
      "CheckinCreateRequest": {
        "type": "object",
        "required": [
          "customer_id",
          "vehicle_id"
        ],
        "description": "Body of `POST /v1/checkins`. Server-managed fields (`id`,\n`company_id`, `created_by`, `seq_number`, `status`,\ntimestamps) are NOT accepted. Line items start empty — a\ndedicated sub-resource will land later.\n",
        "properties": {
          "customer_id": {
            "type": "string"
          },
          "vehicle_id": {
            "type": "string"
          },
          "title": {
            "type": [
              "string",
              "null"
            ]
          },
          "comments": {
            "type": [
              "string",
              "null"
            ]
          },
          "internal_comments": {
            "type": [
              "string",
              "null"
            ]
          },
          "work_category_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "fuel_level": {
            "type": [
              "integer",
              "null"
            ]
          },
          "section1": {
            "type": [
              "string",
              "null"
            ]
          },
          "section2": {
            "type": [
              "string",
              "null"
            ]
          },
          "section3": {
            "type": [
              "string",
              "null"
            ]
          },
          "section4": {
            "type": [
              "string",
              "null"
            ]
          },
          "section5": {
            "type": [
              "string",
              "null"
            ]
          },
          "section6": {
            "type": [
              "string",
              "null"
            ]
          },
          "section7": {
            "type": [
              "string",
              "null"
            ]
          },
          "section8": {
            "type": [
              "string",
              "null"
            ]
          },
          "km": {
            "type": [
              "number",
              "null"
            ]
          }
        },
        "additionalProperties": false
      },
      "CheckinPatchRequest": {
        "type": "object",
        "description": "Body of `PATCH /v1/checkins/{id}`. Three-state per field:\n\n- Field omitted → leave the entity field untouched.\n- Field present with value `null` → clear the entity field.\n- Field present with a value → set the entity field.\n\nFields not listed here are intentionally NOT patchable: `id`,\n`company_id`, `created_by`, `seq_number`, system timestamps,\n`status` (use the transitions endpoint), `customer_id`,\n`vehicle_id` (immutable), and `line_items` (separate\nsub-resource).\n",
        "properties": {
          "title": {
            "type": [
              "string",
              "null"
            ]
          },
          "comments": {
            "type": [
              "string",
              "null"
            ]
          },
          "internal_comments": {
            "type": [
              "string",
              "null"
            ]
          },
          "fuel_level": {
            "type": [
              "integer",
              "null"
            ]
          },
          "section1": {
            "type": [
              "string",
              "null"
            ]
          },
          "section2": {
            "type": [
              "string",
              "null"
            ]
          },
          "section3": {
            "type": [
              "string",
              "null"
            ]
          },
          "section4": {
            "type": [
              "string",
              "null"
            ]
          },
          "section5": {
            "type": [
              "string",
              "null"
            ]
          },
          "section6": {
            "type": [
              "string",
              "null"
            ]
          },
          "section7": {
            "type": [
              "string",
              "null"
            ]
          },
          "section8": {
            "type": [
              "string",
              "null"
            ]
          },
          "km": {
            "type": [
              "number",
              "null"
            ]
          },
          "work_category_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "worksheet_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "quote_id": {
            "type": [
              "string",
              "null"
            ]
          }
        },
        "additionalProperties": false
      },
      "CheckinTransitionRequest": {
        "type": "object",
        "required": [
          "action"
        ],
        "description": "Body of `POST /v1/checkins/{id}/transitions`. The target\nlifecycle state. Unknown values are rejected with 422\n`parameter_invalid`.\n",
        "properties": {
          "action": {
            "type": "string",
            "enum": [
              "active",
              "completed"
            ]
          }
        },
        "additionalProperties": false
      },
      "CheckinLineItemInput": {
        "type": "object",
        "required": [
          "description"
        ],
        "description": "A checkin line item supplied in an `add` or `update`. Checkin\nline items are description-only notes — no quantity, price, VAT,\narticle, or position.\n",
        "properties": {
          "description": {
            "type": "string",
            "description": "Required, non-empty."
          }
        },
        "additionalProperties": false
      },
      "CheckinLineItemUpdate": {
        "description": "Identifies an existing checkin line item by `id` and carries its\nreplacement description. Applied as a remove-then-add internally,\nso the updated line receives a NEW id in the response.\n",
        "allOf": [
          {
            "$ref": "#/components/schemas/CheckinLineItemInput"
          },
          {
            "type": "object",
            "required": [
              "id"
            ],
            "properties": {
              "id": {
                "type": "string"
              }
            }
          }
        ]
      },
      "CheckinLineItemsBatchRequest": {
        "type": "object",
        "description": "Body of `POST /v1/checkins/{id}/line-items/batch`. Applies a\nsingle diff against the checkin's current line items and returns\nthe full checkin. Narrower than the worksheet/quote batch: no\n`reorder` (rows have no position), description-only fields.\n\nAll arrays are optional; an empty body is a no-op. An id may\nappear in at most one of `remove`/`update`, and ids in\n`remove`/`update` must belong to the target checkin.\n",
        "properties": {
          "add": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CheckinLineItemInput"
            }
          },
          "update": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CheckinLineItemUpdate"
            }
          },
          "remove": {
            "type": "array",
            "description": "Ids of line items to delete.",
            "items": {
              "type": "string"
            }
          }
        },
        "additionalProperties": false
      },
      "CalendarEntryCreateRequest": {
        "type": "object",
        "required": [
          "title",
          "start_time",
          "end_time",
          "color"
        ],
        "description": "Body of `POST /v1/calendar-entries`. `end_time` must be\nstrictly after `start_time`. `user_id` (ownership),\n`company_id`, and audit timestamps are server-managed and\nnot accepted on the wire.\n\n`worksheet_id` links the appointment to a scheda lavoro.\n`customer_id` and `vehicle_id`, when sent, must be the\nworksheet's (a worksheet without a vehicle fits any vehicle of\nits customer); when omitted they are taken from the worksheet.\nA mismatch returns 422 `parameter_invalid` on `/worksheet_id`.\n\n`quote_id` / `checkin_id` are response-only: the underlying\nrepository does not persist them on create today, so they are\nintentionally absent from this request.\n",
        "properties": {
          "title": {
            "type": "string"
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "start_time": {
            "$ref": "#/components/schemas/WritableTimestamp"
          },
          "end_time": {
            "$ref": "#/components/schemas/WritableTimestamp"
          },
          "color": {
            "type": "string"
          },
          "email_reminder": {
            "type": "boolean"
          },
          "customer_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "vehicle_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "worksheet_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "work_category_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "calendar_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "auto_reminder_enabled": {
            "type": "boolean"
          },
          "auto_reminder_channel": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "email",
              "sms",
              "whatsapp",
              null
            ]
          }
        },
        "additionalProperties": false
      },
      "CalendarEntryPatchRequest": {
        "type": "object",
        "description": "Body of `PATCH /v1/calendar-entries/{id}`. Three-state PATCH\nsemantics: omitted = unchanged, explicit `null` = clear (only\nfor nullable fields). Sending `null` on a non-nullable field\n(`title`, `color`, `start_time`, `end_time`,\n`email_reminder`, `auto_reminder_enabled`) returns 422\n`parameter_invalid`. When `start_time` or `end_time` is\npatched the resulting pair must satisfy\n`end_time \u003e start_time`.\n\n`worksheet_id` links the appointment to a scheda lavoro;\n`null` unlinks. The customer and vehicle after the patch must\nbe the worksheet's (a worksheet without a vehicle fits any\nvehicle of its customer); the ones the entry is left without\nare taken from the worksheet, and a mismatch returns 422\n`parameter_invalid` on `/worksheet_id`. A patch that moves\n`customer_id` or `vehicle_id` off the linked worksheet, without\nsending `worksheet_id`, unlinks it.\n",
        "properties": {
          "title": {
            "type": "string"
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "start_time": {
            "$ref": "#/components/schemas/WritableTimestamp"
          },
          "end_time": {
            "$ref": "#/components/schemas/WritableTimestamp"
          },
          "color": {
            "type": "string"
          },
          "email_reminder": {
            "type": "boolean"
          },
          "customer_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "vehicle_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "worksheet_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "work_category_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "calendar_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "auto_reminder_enabled": {
            "type": "boolean"
          },
          "auto_reminder_channel": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "email",
              "sms",
              "whatsapp",
              null
            ]
          }
        },
        "additionalProperties": false
      },
      "AuthMethodResponse": {
        "type": "object",
        "required": [
          "object",
          "email",
          "method"
        ],
        "properties": {
          "object": {
            "type": "string",
            "const": "auth_method",
            "description": "Polymorphic discriminator."
          },
          "email": {
            "type": "string",
            "format": "email",
            "description": "Echoes the email from the request."
          },
          "method": {
            "type": "string",
            "enum": [
              "password",
              "sign_on_token",
              "not_found"
            ],
            "description": "Credential step to show next. `password` → submit to\n`/v1/auth/login`; `sign_on_token` → request a code via\n`/v1/auth/sign-on-token`, then submit to\n`/v1/auth/login-with-token`; `not_found` → no account for\nthis email, show the contact-assistance sheet (web-parity\ndisclosure — the public login already reveals this).\n"
          }
        }
      },
      "AuthTokenResponse": {
        "type": "object",
        "required": [
          "object",
          "token_type",
          "access_token",
          "expires_at",
          "user"
        ],
        "properties": {
          "object": {
            "type": "string",
            "const": "auth_token",
            "description": "Polymorphic discriminator."
          },
          "token_type": {
            "type": "string",
            "const": "Bearer"
          },
          "access_token": {
            "type": "string",
            "description": "Mobile JWT. Send as `Authorization: Bearer \u003caccess_token\u003e`\non subsequent requests. Carries `aud=mobile`.\n",
            "example": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
          },
          "expires_at": {
            "type": "string",
            "format": "date-time",
            "description": "Absolute expiration timestamp in ISO 8601 / RFC 3339 UTC.\nClients should refresh a few minutes before this elapses.\n",
            "example": "2026-06-05T00:00:00Z"
          },
          "user": {
            "$ref": "#/components/schemas/User"
          }
        }
      },
      "SignOnTokenResponse": {
        "type": "object",
        "required": [
          "object",
          "email",
          "message"
        ],
        "properties": {
          "object": {
            "type": "string",
            "const": "sign_on_token_request"
          },
          "email": {
            "type": "string",
            "format": "email",
            "description": "Echoes the email from the request."
          },
          "message": {
            "type": "string",
            "description": "Human-readable confirmation string."
          }
        }
      },
      "User": {
        "type": "object",
        "required": [
          "object",
          "id",
          "name",
          "email"
        ],
        "properties": {
          "object": {
            "type": "string",
            "const": "user"
          },
          "id": {
            "type": "string",
            "description": "Stable opaque ID (UUID at the entity layer).",
            "example": "9921d5f7-83ba-4c60-a1e2-cf7b4d0925a8"
          },
          "name": {
            "type": "string",
            "description": "Display name (first + last joined)."
          },
          "email": {
            "type": "string",
            "format": "email"
          },
          "role": {
            "type": "string",
            "description": "Workspace role. Omitted for users without a role set.\n"
          }
        }
      },
      "Customer": {
        "type": "object",
        "required": [
          "object",
          "id",
          "customer_type",
          "created_at",
          "updated_at"
        ],
        "description": "A workshop's client. Multi-tenant: belongs to one company.\nMost descriptive fields are optional (nullable) because\ncustomer records are commonly created from a phone call\nwith only a name and phone number.\n",
        "properties": {
          "object": {
            "type": "string",
            "const": "customer"
          },
          "id": {
            "type": "string",
            "example": "4c911f08-7713-4e83-bdf6-a0692a1b43fd"
          },
          "customer_type": {
            "type": "string",
            "description": "Tax-status discriminator (e.g. \"private\", \"company\")."
          },
          "full_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "email": {
            "type": [
              "string",
              "null"
            ],
            "format": "email"
          },
          "phone": {
            "type": [
              "string",
              "null"
            ],
            "description": "Stored E.164 value, e.g. \"+393331234567\"."
          },
          "phone_dial_code": {
            "type": [
              "string",
              "null"
            ],
            "description": "`phone` split server-side for the edit form's dial-code\nselector (e.g. \"+39\"). Null when `phone` is null.\n"
          },
          "phone_national": {
            "type": [
              "string",
              "null"
            ],
            "description": "`phone` minus the dial code, for the edit form's\nnational-number input. Null when `phone` is null.\n"
          },
          "address": {
            "type": [
              "string",
              "null"
            ]
          },
          "city": {
            "type": [
              "string",
              "null"
            ]
          },
          "zip_code": {
            "type": [
              "string",
              "null"
            ]
          },
          "province": {
            "type": [
              "string",
              "null"
            ]
          },
          "tax_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "Italian VAT number (partita IVA) for companies."
          },
          "fiscal_code": {
            "type": [
              "string",
              "null"
            ],
            "description": "Italian fiscal code (codice fiscale) for individuals."
          },
          "pec": {
            "type": [
              "string",
              "null"
            ],
            "description": "Italian certified-email address for e-invoicing."
          },
          "destination_code": {
            "type": [
              "string",
              "null"
            ],
            "description": "Italian SDI destination code for e-invoicing."
          },
          "payment_method": {
            "type": [
              "string",
              "null"
            ]
          },
          "comments": {
            "type": [
              "string",
              "null"
            ]
          },
          "birthday": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "last_vehicle_name": {
            "type": [
              "string",
              "null"
            ],
            "description": "Display string of the customer's most recent vehicle\n(e.g. \"Fiat Panda\"). Pre-computed on the entity for\nlist-row rendering without a second request.\n"
          },
          "last_vehicle_license": {
            "type": [
              "string",
              "null"
            ]
          },
          "total_spent": {
            "type": "number",
            "format": "double",
            "description": "Lifetime spend (worksheet totals net of discounts), computed\nby the list query for row rendering. Money — render via the\nlocale formatter; avoid arithmetic.\n"
          },
          "documents": {
            "type": "array",
            "description": "Attachments (Documenti Allegati). Present on the detail\nresponse only; omitted on lists.\n",
            "items": {
              "$ref": "#/components/schemas/CustomerDocument"
            }
          },
          "next_appointment": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          },
          "deleted_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "Soft-deletion timestamp. Populated on the response from\nDELETE /v1/customers/{id}. Customers with a non-null\ndeleted_at are not returned by list/detail endpoints —\nthis field appears for the delete response only.\n"
          }
        }
      },
      "ReportWidgetEnvelope": {
        "type": "object",
        "required": [
          "object",
          "widget",
          "preset",
          "from",
          "to",
          "data"
        ],
        "description": "Shell of every report-widget response. `preset`/`from`/`to` echo\nthe date filter the server applied; `from`/`to` are always\nconcrete dates —\n`all` resolves to company-creation → today. `data` carries the\nwidget-specific shape (one of the Report* schemas below, keyed\nby the widget path parameter). New widgets add a key and a Data\nschema; the envelope never changes.\n",
        "properties": {
          "object": {
            "type": "string",
            "const": "report_widget"
          },
          "widget": {
            "type": "string"
          },
          "preset": {
            "type": "string"
          },
          "from": {
            "type": "string",
            "format": "date"
          },
          "to": {
            "type": "string",
            "format": "date"
          },
          "data": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/ReportKPIs"
              },
              {
                "$ref": "#/components/schemas/ReportChart"
              },
              {
                "$ref": "#/components/schemas/ReportQuoteConversion"
              },
              {
                "$ref": "#/components/schemas/ReportWorksheetStatus"
              },
              {
                "$ref": "#/components/schemas/ReportOutstandingWorksheets"
              },
              {
                "$ref": "#/components/schemas/ReportAppointments"
              },
              {
                "$ref": "#/components/schemas/ReportRankedList"
              }
            ]
          }
        }
      },
      "ReportKPIs": {
        "type": "object",
        "required": [
          "kpis"
        ],
        "description": "Data of widget `kpis`.",
        "properties": {
          "kpis": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "key",
                "value",
                "is_currency",
                "previous_value",
                "delta",
                "delta_percentage"
              ],
              "properties": {
                "key": {
                  "type": "string",
                  "enum": [
                    "sales",
                    "outstanding",
                    "worksheets",
                    "customers"
                  ]
                },
                "value": {
                  "type": "number"
                },
                "is_currency": {
                  "type": "boolean"
                },
                "previous_value": {
                  "type": "number"
                },
                "delta": {
                  "type": "number"
                },
                "delta_percentage": {
                  "type": "number"
                }
              }
            }
          }
        }
      },
      "ReportChart": {
        "type": "object",
        "required": [
          "x_axis",
          "series"
        ],
        "description": "Data of the stacked-chart widgets (`sales-chart`,\n`expenses-chart`, `worksheet-margin-chart`). `x_axis` entries\nare YYYY-MM-DD bucket starts (daily ≤31-day periods, else\nmonthly); each series' `values` align with `x_axis`.\n",
        "properties": {
          "x_axis": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "series": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "key",
                "values"
              ],
              "properties": {
                "key": {
                  "type": "string"
                },
                "values": {
                  "type": "array",
                  "items": {
                    "type": "number"
                  }
                }
              }
            }
          }
        }
      },
      "ReportQuoteConversion": {
        "type": "object",
        "required": [
          "sent",
          "rejected",
          "converted",
          "conversion_rate"
        ],
        "description": "Data of widget `quote-conversion`.",
        "properties": {
          "sent": {
            "type": "integer"
          },
          "rejected": {
            "type": "integer"
          },
          "converted": {
            "type": "integer"
          },
          "conversion_rate": {
            "type": "number"
          }
        }
      },
      "ReportWorksheetStatus": {
        "type": "object",
        "required": [
          "draft",
          "waiting",
          "started",
          "completed",
          "delivered",
          "completion_rate"
        ],
        "description": "Data of widget `worksheet-status`.",
        "properties": {
          "draft": {
            "type": "integer"
          },
          "waiting": {
            "type": "integer"
          },
          "started": {
            "type": "integer"
          },
          "completed": {
            "type": "integer"
          },
          "delivered": {
            "type": "integer"
          },
          "completion_rate": {
            "type": "number"
          }
        }
      },
      "ReportOutstandingWorksheets": {
        "type": "object",
        "required": [
          "worksheets"
        ],
        "description": "Data of widget `outstanding-worksheets`.",
        "properties": {
          "worksheets": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "worksheet_id",
                "title",
                "customer_name",
                "outstanding",
                "total",
                "bar_width"
              ],
              "properties": {
                "worksheet_id": {
                  "type": "string"
                },
                "title": {
                  "type": "string"
                },
                "customer_name": {
                  "type": "string"
                },
                "outstanding": {
                  "type": "number"
                },
                "total": {
                  "type": "number"
                },
                "bar_width": {
                  "type": "number",
                  "description": "Server-normalized bar length, 0-100."
                }
              }
            }
          }
        }
      },
      "ReportAppointments": {
        "type": "object",
        "required": [
          "appointments"
        ],
        "description": "Data of widget `upcoming-appointments`.",
        "properties": {
          "appointments": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "id",
                "title",
                "customer_name",
                "start_time"
              ],
              "properties": {
                "id": {
                  "type": "string"
                },
                "title": {
                  "type": "string"
                },
                "customer_name": {
                  "type": "string"
                },
                "start_time": {
                  "type": "string",
                  "format": "date-time"
                }
              }
            }
          }
        }
      },
      "ReportRankedList": {
        "type": "object",
        "required": [
          "rows"
        ],
        "description": "Data of the ranked-list widgets (`top-customers`,\n`top-services`, `worksheet-margins`). `amount` carries the sort\nmetric; `bar_width` is server-normalized 0-100. The margins\nwidget also fills `sublabel` (document number) and `percent`.\n",
        "properties": {
          "rows": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "id",
                "label",
                "amount",
                "bar_width"
              ],
              "properties": {
                "id": {
                  "type": "string"
                },
                "label": {
                  "type": "string"
                },
                "sublabel": {
                  "type": "string"
                },
                "amount": {
                  "type": "number"
                },
                "percent": {
                  "type": "number"
                },
                "bar_width": {
                  "type": "number"
                }
              }
            }
          }
        }
      },
      "BillingInfo": {
        "type": "object",
        "required": [
          "object",
          "state",
          "is_legacy_plan",
          "cancel_at_period_end",
          "unlimited_credits"
        ],
        "properties": {
          "object": {
            "type": "string",
            "const": "billing_info"
          },
          "state": {
            "type": "string",
            "enum": [
              "trial",
              "trial_expired",
              "active",
              "payment_processing",
              "payment_issue"
            ]
          },
          "plan_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "is_legacy_plan": {
            "type": "boolean"
          },
          "billing_interval": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "monthly",
              "yearly",
              null
            ]
          },
          "trial_ends_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "current_period_end": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "cancel_at_period_end": {
            "type": "boolean"
          },
          "credit_balance": {
            "type": [
              "number",
              "null"
            ]
          },
          "unlimited_credits": {
            "type": "boolean"
          }
        }
      },
      "SupportChannel": {
        "type": "object",
        "description": "One support channel's schedule and current availability. Labels\narrive display-ready in Italian; `open_now` is evaluated in Rome\ntime at request time, so the app renders availability badges\nwithout owning the schedule.\n",
        "required": [
          "schedule_label",
          "open_now",
          "response_time"
        ],
        "properties": {
          "schedule_label": {
            "type": "string",
            "example": "Dal lunedì al sabato dalle 9:00 alle 21:00"
          },
          "open_now": {
            "type": "boolean"
          },
          "response_time": {
            "type": "string",
            "example": "30 minuti"
          }
        }
      },
      "SupportInfo": {
        "type": "object",
        "required": [
          "object",
          "agent_name",
          "email",
          "whatsapp_number",
          "email_channel",
          "whatsapp_channel",
          "team_note",
          "priority_support"
        ],
        "properties": {
          "object": {
            "type": "string",
            "const": "support_info"
          },
          "agent_name": {
            "type": "string"
          },
          "email": {
            "type": "string"
          },
          "whatsapp_number": {
            "type": "string",
            "description": "Display form, e.g. \"+39 351 761 1637\"."
          },
          "email_channel": {
            "$ref": "#/components/schemas/SupportChannel"
          },
          "whatsapp_channel": {
            "allOf": [
              {
                "$ref": "#/components/schemas/SupportChannel"
              }
            ],
            "description": "`open_now` covers calls — outside the window messages still\nwork (badge \"Solo messaggi\").\n"
          },
          "team_note": {
            "type": "string",
            "description": "Ready-made availability footer for the support hub."
          },
          "priority_support": {
            "type": "boolean"
          }
        }
      },
      "Calendar": {
        "type": "object",
        "required": [
          "object",
          "id",
          "name",
          "default_color"
        ],
        "description": "A calendar container grouping entries (e.g. Riparazioni,\nRevisioni). Entries carry their own `color`; `default_color`\nis the calendar's colour used for its filter dot and as the\ndefault for new entries.\n",
        "properties": {
          "object": {
            "type": "string",
            "const": "calendar"
          },
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "default_color": {
            "type": "string"
          }
        }
      },
      "Manufacturer": {
        "type": "object",
        "required": [
          "object",
          "id",
          "name"
        ],
        "description": "A vehicle make from the manufacturer catalog (global rows plus\ncompany-specific additions). Read-only on this tier.\n",
        "properties": {
          "object": {
            "type": "string",
            "const": "manufacturer"
          },
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          }
        }
      },
      "Article": {
        "type": "object",
        "required": [
          "object",
          "id",
          "code",
          "name",
          "kind",
          "unit_type",
          "stock",
          "sale_price",
          "vat_code",
          "unit_discount_rate"
        ],
        "description": "A catalog row backing both the magazzino (parts) and the listino\nmanodopera (labor) — `kind` discriminates the two from the unit\ntype. `sale_price` is the NET unit price (VAT excluded), with\nthe customer's price-list override applied when the request\ncarried a `customer_id`; feed it straight into a line item's\n`unit_price`.\n",
        "properties": {
          "object": {
            "type": "string",
            "const": "article"
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "code": {
            "type": [
              "string",
              "null"
            ]
          },
          "name": {
            "type": "string"
          },
          "kind": {
            "type": "string",
            "enum": [
              "item",
              "work"
            ],
            "description": "\"item\" = magazzino (kg, L, kit, pz); \"work\" = manodopera (gg, h, lav, min)."
          },
          "unit_type": {
            "type": "string"
          },
          "stock": {
            "type": "number"
          },
          "sale_price": {
            "type": [
              "number",
              "null"
            ]
          },
          "vat_code": {
            "type": "string"
          },
          "unit_discount_rate": {
            "type": [
              "integer",
              "null"
            ]
          },
          "minimum_stock": {
            "type": [
              "number",
              "null"
            ]
          },
          "purchase_price": {
            "type": [
              "number",
              "null"
            ]
          },
          "location": {
            "type": [
              "string",
              "null"
            ]
          },
          "comments": {
            "type": [
              "string",
              "null"
            ]
          },
          "supplier_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "supplier_name": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "SendContext": {
        "type": "object",
        "required": [
          "object",
          "email",
          "sms",
          "whatsapp",
          "credit_balance"
        ],
        "description": "Defaults for the Invia al cliente sheet (quotes and worksheets):\nrecipients, precompiled bodies (share link included), the\nread-only WhatsApp template preview, per-channel credit costs\n(0 = included in the plan) and the current balance.\n\nSMS/WhatsApp recipients are Italian-only: when the stored phone\nis empty or +39 the client shows an editable +39 national-number\nfield (the typed number is backfilled onto the customer on a\nsuccessful send when previously empty); a stored non-+39 number\nrenders read-only with the send disabled (web parity).\n",
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "quote_send_context",
              "worksheet_send_context"
            ]
          },
          "email": {
            "type": "object",
            "required": [
              "recipient",
              "subject",
              "body",
              "senders"
            ],
            "properties": {
              "recipient": {
                "type": "string"
              },
              "subject": {
                "type": "string"
              },
              "body": {
                "type": "string"
              },
              "senders": {
                "type": "array",
                "description": "The web \"Mittente\" dropdown options: internal always;\nsmtp/gmail when the company configured them (in the web\nsettings — configuration is not exposed here). Exactly\none entry has default=true.\n",
                "items": {
                  "type": "object",
                  "required": [
                    "key",
                    "label",
                    "default"
                  ],
                  "properties": {
                    "key": {
                      "type": "string",
                      "enum": [
                        "internal",
                        "smtp",
                        "gmail"
                      ]
                    },
                    "label": {
                      "type": "string",
                      "example": "Account Gmail (x@gmail.com)"
                    },
                    "default": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "sms": {
            "type": "object",
            "required": [
              "recipient",
              "body",
              "max_length",
              "credit_cost"
            ],
            "properties": {
              "recipient": {
                "type": "string"
              },
              "body": {
                "type": "string"
              },
              "max_length": {
                "type": "integer",
                "const": 160
              },
              "credit_cost": {
                "type": "number"
              }
            }
          },
          "whatsapp": {
            "type": "object",
            "required": [
              "recipient",
              "template_preview",
              "credit_cost"
            ],
            "properties": {
              "recipient": {
                "type": "string"
              },
              "template_preview": {
                "type": "string",
                "description": "Pre-approved template — display-only."
              },
              "credit_cost": {
                "type": "number"
              }
            }
          },
          "credit_balance": {
            "type": "number"
          }
        }
      },
      "SendRequest": {
        "type": "object",
        "required": [
          "channel",
          "recipient"
        ],
        "properties": {
          "channel": {
            "type": "string",
            "enum": [
              "email",
              "sms",
              "whatsapp"
            ]
          },
          "recipient": {
            "type": "string"
          },
          "subject": {
            "type": "string",
            "maxLength": 50,
            "description": "email only (required there)."
          },
          "message": {
            "type": "string",
            "description": "email/sms body; ignored for whatsapp."
          },
          "sender_method": {
            "type": "string",
            "enum": [
              "internal",
              "smtp",
              "gmail"
            ],
            "description": "email only; omitted = company default. A missing or broken\nsmtp/gmail configuration falls back to the internal sender\n(web parity).\n"
          }
        }
      },
      "SendResult": {
        "type": "object",
        "required": [
          "object",
          "channel",
          "recipient",
          "credits_spent",
          "credit_balance"
        ],
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "quote_send",
              "worksheet_send"
            ]
          },
          "channel": {
            "type": "string",
            "enum": [
              "email",
              "sms",
              "whatsapp"
            ]
          },
          "recipient": {
            "type": "string"
          },
          "credits_spent": {
            "type": "number"
          },
          "credit_balance": {
            "type": "number"
          }
        }
      },
      "CustomerMessage": {
        "type": "object",
        "required": [
          "object",
          "id",
          "channel",
          "title",
          "preview",
          "content",
          "recipient",
          "sent_at"
        ],
        "description": "One entry of the document's send log (Messaggi Cliente).",
        "properties": {
          "object": {
            "type": "string",
            "const": "customer_message"
          },
          "id": {
            "type": "string"
          },
          "channel": {
            "type": "string",
            "enum": [
              "email",
              "sms",
              "whatsapp"
            ]
          },
          "title": {
            "type": [
              "string",
              "null"
            ],
            "description": "Email subject; null otherwise."
          },
          "preview": {
            "type": "string"
          },
          "content": {
            "type": "string"
          },
          "recipient": {
            "type": "string"
          },
          "sent_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "ShareLink": {
        "type": "object",
        "required": [
          "object",
          "url"
        ],
        "description": "Public customer-facing URL of a document. Get-or-create, so the\nURL is stable across calls; append it to the message when sending\nvia a device channel (WhatsApp / SMS / email deep links).\n",
        "properties": {
          "object": {
            "type": "string",
            "const": "share_link"
          },
          "url": {
            "type": "string",
            "example": "https://officina.it/dl/aB3xK9"
          }
        }
      },
      "Sezionale": {
        "type": "object",
        "required": [
          "object",
          "id",
          "code",
          "label"
        ],
        "properties": {
          "object": {
            "type": "string",
            "const": "sezionale"
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "code": {
            "type": "string"
          },
          "label": {
            "type": "string"
          }
        }
      },
      "WorkCategory": {
        "type": "object",
        "required": [
          "object",
          "id",
          "name"
        ],
        "properties": {
          "object": {
            "type": "string",
            "const": "work_category"
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          }
        }
      },
      "VatRate": {
        "type": "object",
        "required": [
          "object",
          "code",
          "rate",
          "description"
        ],
        "description": "A VAT rate enabled for the company. `code` is what line items\nreference via `vat_code`; `rate` is for display and client-side\ntotal previews — the server always re-resolves the rate from\nthe code when persisting.\n",
        "properties": {
          "object": {
            "type": "string",
            "const": "vat_rate"
          },
          "code": {
            "type": "string"
          },
          "rate": {
            "type": "number"
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "Vehicle": {
        "type": "object",
        "required": [
          "object",
          "id",
          "manufacturer_id",
          "manufacturer_name",
          "model",
          "is_rental",
          "created_at",
          "updated_at"
        ],
        "description": "A vehicle owned (or managed) by a workshop client.\nMulti-tenant: belongs to one company. `manufacturer_name`\nand `customer_name` are joined-in convenience fields\npopulated by the repository so list rows can render\n`Fiat Panda — Mario Rossi` without a follow-up request.\n\nRental fields are read-only on this resource. Mutate them\nvia the dedicated rental setup flow.\n",
        "properties": {
          "object": {
            "type": "string",
            "const": "vehicle"
          },
          "id": {
            "type": "string",
            "example": "2200ab13-7c48-4d52-b6e9-0a4f81c27d3e"
          },
          "license": {
            "type": [
              "string",
              "null"
            ],
            "description": "License plate (regional format, free-form string)."
          },
          "manufacturer_id": {
            "type": "string"
          },
          "manufacturer_name": {
            "type": "string",
            "description": "Joined from the manufacturers table for display.\nRead-only; mutate via `manufacturer_id`.\n"
          },
          "model": {
            "type": "string"
          },
          "variant": {
            "type": [
              "string",
              "null"
            ]
          },
          "vin": {
            "type": [
              "string",
              "null"
            ],
            "description": "Vehicle Identification Number."
          },
          "transmission": {
            "type": [
              "string",
              "null"
            ]
          },
          "engine_power_kw": {
            "type": [
              "integer",
              "null"
            ]
          },
          "engine_displacement_ccm": {
            "type": [
              "integer",
              "null"
            ]
          },
          "vehicle_weight_kg": {
            "type": [
              "integer",
              "null"
            ]
          },
          "engine_category": {
            "type": [
              "string",
              "null"
            ]
          },
          "registered_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "comments": {
            "type": [
              "string",
              "null"
            ]
          },
          "customer_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "customer_name": {
            "type": [
              "string",
              "null"
            ],
            "description": "Joined from the owning customer's `full_name` for\ndisplay. Read-only; mutate via `customer_id`.\n"
          },
          "km": {
            "type": [
              "number",
              "null"
            ],
            "description": "Most recently recorded odometer reading."
          },
          "km_registered_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "last_inspection_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "next_inspection_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "next_appointment": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "Earliest upcoming calendar appointment for this vehicle,\ncomputed at read time.\n"
          },
          "custom_fields": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": true,
            "description": "Free-form key/value blob stored as one JSON column."
          },
          "custom_field_values": {
            "type": "array",
            "description": "Detail responses only. The company's visible custom field\ndefinitions (settings → Campi veicolo) resolved against\nthis vehicle: configured order, display-ready names, \"\"\nwhen the vehicle has no value. Clients render these\ndirectly — no definition lookup needed.\n",
            "items": {
              "type": "object",
              "required": [
                "id",
                "name",
                "type",
                "value"
              ],
              "properties": {
                "id": {
                  "type": "string"
                },
                "name": {
                  "type": "string"
                },
                "type": {
                  "type": "string",
                  "enum": [
                    "string",
                    "integer",
                    "datetime"
                  ]
                },
                "value": {
                  "type": "string"
                }
              }
            }
          },
          "documents": {
            "type": "array",
            "description": "Detail responses only — the vehicle's Documenti Allegati.\n`url` is the authenticated download route\n(`/v1/vehicles/{id}/documents/{documentID}/download`).\n",
            "items": {
              "$ref": "#/components/schemas/VehicleDocument"
            }
          },
          "field_visibility": {
            "type": "object",
            "description": "Detail responses only. The company's optional-field\ntoggles (settings → Campi veicolo): the edit form hides —\nand omits from its PATCH — exactly what the web form\nhides. All true when never configured.\n",
            "properties": {
              "km": {
                "type": "boolean"
              },
              "numero_telaio": {
                "type": "boolean"
              },
              "transmission": {
                "type": "boolean"
              },
              "engine_category": {
                "type": "boolean"
              },
              "engine_power_kw": {
                "type": "boolean"
              },
              "engine_displacement_ccm": {
                "type": "boolean"
              },
              "vehicle_weight_kg": {
                "type": "boolean"
              },
              "registration_date": {
                "type": "boolean"
              },
              "last_inspection_date": {
                "type": "boolean"
              },
              "next_inspection_date": {
                "type": "boolean"
              }
            }
          },
          "is_rental": {
            "type": "boolean",
            "description": "Whether this vehicle is part of the workshop's rental\nfleet. Read-only here — managed by the rental setup flow.\n"
          },
          "rental_daily_price_before_tax": {
            "type": [
              "number",
              "null"
            ]
          },
          "rental_daily_price_tax": {
            "type": [
              "number",
              "null"
            ]
          },
          "rental_daily_price_after_tax": {
            "type": [
              "number",
              "null"
            ]
          },
          "rental_vat_code": {
            "type": [
              "string",
              "null"
            ]
          },
          "rental_daily_discount_rate": {
            "type": [
              "number",
              "null"
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          },
          "deleted_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "Soft-deletion timestamp. Populated on the response from\nDELETE /v1/vehicles/{id}. Vehicles with a non-null\ndeleted_at are not returned by list/detail endpoints —\nthis field appears for the delete response only.\n"
          }
        }
      },
      "Worksheet": {
        "type": "object",
        "required": [
          "object",
          "id",
          "seq_number",
          "status",
          "customer_id",
          "created_by",
          "created_at",
          "updated_at"
        ],
        "description": "A workshop work order. Belongs to one customer and (usually)\none vehicle. Lifecycle: draft → waiting → started →\ncompleted → delivered → archived. Each state has a matching\nnullable `*_at` timestamp populated by the server when the\ntransition fires.\n\nTotals are server-computed from the embedded line items and\nrounded to the working currency precision. They are\nread-only on the wire.\n\n`line_items` is populated on detail / write responses and\nomitted (or `null`) on list responses to keep page payloads\nsmall.\n",
        "properties": {
          "object": {
            "type": "string",
            "const": "worksheet"
          },
          "id": {
            "type": "string",
            "example": "a5d21e64-8f37-4b90-ae12-3c7d6b048f95"
          },
          "seq_number": {
            "type": "integer",
            "description": "Per-company human-friendly sequence number. Allocated\nserver-side at creation; never reused.\n"
          },
          "status": {
            "type": "string",
            "enum": [
              "draft",
              "waiting",
              "started",
              "completed",
              "delivered",
              "archived"
            ]
          },
          "title": {
            "type": [
              "string",
              "null"
            ]
          },
          "comments": {
            "type": [
              "string",
              "null"
            ]
          },
          "internal_comments": {
            "type": [
              "string",
              "null"
            ],
            "description": "Workshop-internal notes; surfaced through the mobile\napp for the workshop's own users (no customer-facing\nchannel exposes this field).\n"
          },
          "customer_id": {
            "type": "string"
          },
          "vehicle_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "assigned_to": {
            "type": [
              "string",
              "null"
            ]
          },
          "work_category_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "km": {
            "type": [
              "number",
              "null"
            ],
            "description": "Odometer reading captured when the worksheet was opened.\nFree-form decimal because some workshops record fuel\nmileage or hour-meter values in the same field.\n"
          },
          "created_by": {
            "type": "string"
          },
          "created_by_full_name": {
            "type": "string",
            "description": "Display string of the user who created the worksheet —\ndenormalized for list rendering without a join.\n"
          },
          "customer_name": {
            "type": "string"
          },
          "vehicle_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "vehicle_license": {
            "type": [
              "string",
              "null"
            ]
          },
          "work_category_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "total_before_tax": {
            "type": "number"
          },
          "total_tax": {
            "type": "number"
          },
          "total_after_tax": {
            "type": "number"
          },
          "total_after_discount": {
            "type": "number"
          },
          "total_discount": {
            "type": "number"
          },
          "total_price": {
            "type": "number"
          },
          "total_payments": {
            "type": "number"
          },
          "outstanding_payments": {
            "type": "number"
          },
          "fiscal_document_type": {
            "type": "string",
            "description": "Which fiscal document the worksheet produces: \"fattura\"\n(default) or \"scontrino\".\n"
          },
          "invoice_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "Detail responses only — the first linked invoice, when one\nexists. Drives the Vedi Fattura action.\n"
          },
          "receipt_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "Detail responses only — the linked scontrino for\nscontrino-type worksheets, when one exists.\n"
          },
          "linked_invoices": {
            "type": "array",
            "description": "Detail responses only — compact rows for the Fattura tab\n(fattura-type worksheets).\n",
            "items": {
              "$ref": "#/components/schemas/WorksheetFiscalDocument"
            }
          },
          "linked_receipt": {
            "description": "Detail responses only — the compact row for the Scontrino\ntab (scontrino-type worksheets), when one exists.\n",
            "allOf": [
              {
                "$ref": "#/components/schemas/WorksheetFiscalDocument"
              }
            ]
          },
          "frozen": {
            "type": "boolean",
            "description": "Detail responses only (since 2026-07-19) — true when a linked\ninvoice has a non-draft electronic invoice; mutations are\nrejected with 409 and the app hides/blocks Modifica.\n"
          },
          "total_work_cost": {
            "type": "number",
            "description": "Internal labour cost (Riepilogo Costi)."
          },
          "total_article_cost": {
            "type": "number",
            "description": "Internal parts cost (Riepilogo Costi)."
          },
          "waiting_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "started_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "completed_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "delivered_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "archived_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          },
          "deleted_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "Soft-deletion timestamp. Populated on the response from\nDELETE /v1/worksheets/{id}. Worksheets with a non-null\ndeleted_at are not returned by list/detail endpoints.\n"
          },
          "line_items": {
            "type": [
              "array",
              "null"
            ],
            "description": "Embedded line items. Populated on detail / create / patch\n/ delete / transition responses. List responses set this\nto `null` to keep the payload small.\n",
            "items": {
              "$ref": "#/components/schemas/WorkLineItem"
            }
          },
          "documents": {
            "type": [
              "array",
              "null"
            ],
            "description": "Attachments (Documenti Allegati). Populated on the same\nresponses as `line_items`; null on lists.\n",
            "items": {
              "$ref": "#/components/schemas/WorksheetDocument"
            }
          }
        }
      },
      "WorksheetStatusCounts": {
        "type": "object",
        "required": [
          "object",
          "draft",
          "waiting",
          "started",
          "completed",
          "delivered",
          "archived",
          "date_filter"
        ],
        "properties": {
          "object": {
            "type": "string",
            "const": "worksheet_status_counts"
          },
          "draft": {
            "type": "integer"
          },
          "waiting": {
            "type": "integer"
          },
          "started": {
            "type": "integer"
          },
          "completed": {
            "type": "integer"
          },
          "delivered": {
            "type": "integer"
          },
          "archived": {
            "type": "integer"
          },
          "date_filter": {
            "$ref": "#/components/schemas/DateFilter"
          }
        }
      },
      "QuoteStatusCounts": {
        "type": "object",
        "required": [
          "object",
          "waiting",
          "accepted",
          "rejected",
          "date_filter"
        ],
        "properties": {
          "object": {
            "type": "string",
            "const": "quote_status_counts"
          },
          "waiting": {
            "type": "integer"
          },
          "accepted": {
            "type": "integer"
          },
          "rejected": {
            "type": "integer"
          },
          "date_filter": {
            "$ref": "#/components/schemas/DateFilter"
          }
        }
      },
      "InvoicePaymentCounts": {
        "type": "object",
        "required": [
          "object",
          "pending",
          "settled",
          "date_filter"
        ],
        "description": "Tab counters for the invoices list. pending = outstanding payments \u003e 0, settled = fully paid.",
        "properties": {
          "object": {
            "type": "string",
            "const": "invoice_payment_counts"
          },
          "pending": {
            "type": "integer"
          },
          "settled": {
            "type": "integer"
          },
          "date_filter": {
            "$ref": "#/components/schemas/DateFilter"
          }
        }
      },
      "CustomerDocument": {
        "type": "object",
        "required": [
          "object",
          "id",
          "name",
          "content_type",
          "url",
          "file_size_bytes",
          "created_at"
        ],
        "description": "One customer attachment. `url` is the document's authenticated\ndownload route\n(`/v1/customers/{id}/documents/{documentID}/download`) — fetch it\nwith the bearer to stream the bytes.\n",
        "properties": {
          "object": {
            "type": "string",
            "const": "customer_document"
          },
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "content_type": {
            "type": "string"
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "url": {
            "type": "string",
            "example": "/v1/customers/0b6b…/documents/77c2…/download"
          },
          "file_size_bytes": {
            "type": "integer"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "WorksheetDocument": {
        "type": "object",
        "required": [
          "object",
          "id",
          "name",
          "content_type",
          "url",
          "file_size_bytes",
          "created_at"
        ],
        "description": "One worksheet attachment; `url` opens the stored file.",
        "properties": {
          "object": {
            "type": "string",
            "const": "worksheet_document"
          },
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "content_type": {
            "type": "string"
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "url": {
            "type": "string"
          },
          "file_size_bytes": {
            "type": "integer"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "Payment": {
        "type": "object",
        "required": [
          "object",
          "id",
          "amount",
          "payment_method",
          "payment_date",
          "created_at"
        ],
        "description": "One registered payment on a worksheet's invoice. payment_method\nis an SDI ModalitaPagamento code (MP01–MP23).\n",
        "properties": {
          "object": {
            "type": "string",
            "const": "payment"
          },
          "id": {
            "type": "string"
          },
          "amount": {
            "type": "number",
            "format": "double"
          },
          "payment_method": {
            "type": "string"
          },
          "payment_date": {
            "type": "string",
            "format": "date-time"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "PaymentCreateRequest": {
        "type": "object",
        "required": [
          "amount"
        ],
        "properties": {
          "amount": {
            "type": "number",
            "format": "double",
            "description": "Must be greater than zero."
          },
          "payment_method": {
            "type": "string",
            "description": "MP01–MP23. Omitted/invalid values fall back to the\ncustomer's preferred method, then the company default.\n"
          },
          "payment_date": {
            "type": "string",
            "format": "date",
            "description": "YYYY-MM-DD between 1900-01-01 and 2100-12-31 (same window as\n`WritableTimestamp`); default today. Out of range → 422\n`parameter_invalid`.\n"
          },
          "financial_account_id": {
            "type": "string",
            "description": "Conto di saldo (Banca, Cassa, …) — one of the company's\naccounts from GET /v1/financial-accounts. Optional; an\naccount belonging to another company is rejected with 422.\n"
          }
        },
        "additionalProperties": false
      },
      "WorkLineItem": {
        "type": "object",
        "required": [
          "object",
          "id",
          "description",
          "quantity",
          "unit_type",
          "unit_price",
          "tax_rate",
          "total_price",
          "position",
          "created_at"
        ],
        "description": "A single line on a worksheet. Returned read-only on every\nworksheet response; mutate via\n`POST /v1/worksheets/{id}/line-items/batch`.\n",
        "properties": {
          "object": {
            "type": "string",
            "const": "work_line_item"
          },
          "id": {
            "type": "string"
          },
          "article_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "Optional link to a catalog article. When set, edits\nflow through the article inventory bookkeeping.\n"
          },
          "description": {
            "type": "string"
          },
          "quantity": {
            "type": "number"
          },
          "unit_type": {
            "type": "string"
          },
          "unit_price": {
            "type": "number"
          },
          "unit_discount_rate": {
            "type": "number"
          },
          "tax_rate": {
            "type": "number"
          },
          "vat_code": {
            "type": [
              "string",
              "null"
            ]
          },
          "total_price": {
            "type": "number"
          },
          "total_before_tax": {
            "type": "number"
          },
          "total_tax": {
            "type": "number"
          },
          "total_after_tax": {
            "type": "number"
          },
          "position": {
            "type": "integer"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "Quote": {
        "type": "object",
        "required": [
          "object",
          "id",
          "seq_number",
          "status",
          "customer_id",
          "customer_name",
          "created_at",
          "updated_at"
        ],
        "description": "A customer-facing job estimate. Lifecycle: `waiting` →\n`accepted` / `rejected`. Totals are server-computed from the\nembedded line items and are read-only on the wire.\n",
        "properties": {
          "object": {
            "type": "string",
            "const": "quote"
          },
          "id": {
            "type": "string"
          },
          "seq_number": {
            "type": "integer"
          },
          "status": {
            "type": "string",
            "enum": [
              "waiting",
              "accepted",
              "rejected"
            ]
          },
          "title": {
            "type": [
              "string",
              "null"
            ]
          },
          "comments": {
            "type": [
              "string",
              "null"
            ]
          },
          "internal_comments": {
            "type": [
              "string",
              "null"
            ]
          },
          "customer_id": {
            "type": "string"
          },
          "customer_name": {
            "type": "string"
          },
          "vehicle_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "vehicle_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "vehicle_license": {
            "type": [
              "string",
              "null"
            ]
          },
          "work_category_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "work_category_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "km": {
            "type": [
              "number",
              "null"
            ],
            "format": "double"
          },
          "worksheet_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "worksheet_seq_number": {
            "type": [
              "integer",
              "null"
            ]
          },
          "worksheet_created_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "total_before_tax": {
            "type": "number",
            "format": "double"
          },
          "total_tax": {
            "type": "number",
            "format": "double"
          },
          "total_after_tax": {
            "type": "number",
            "format": "double"
          },
          "total_discount": {
            "type": "number",
            "format": "double"
          },
          "total_discount_before_tax": {
            "type": "number",
            "format": "double"
          },
          "total_after_discount": {
            "type": "number",
            "format": "double"
          },
          "total_work": {
            "type": "number",
            "format": "double"
          },
          "total_articles": {
            "type": "number",
            "format": "double"
          },
          "hide_discounts": {
            "type": "boolean"
          },
          "line_items": {
            "type": [
              "array",
              "null"
            ],
            "description": "Embedded line items. Populated on detail / write\nresponses; list responses set this to `null` to keep\npayloads small.\n",
            "items": {
              "$ref": "#/components/schemas/QuoteLineItem"
            }
          },
          "delivered_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          },
          "archived_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "deleted_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "QuoteLineItem": {
        "type": "object",
        "required": [
          "object",
          "id",
          "description",
          "unit_type",
          "quantity",
          "unit_price",
          "position",
          "created_at"
        ],
        "properties": {
          "object": {
            "type": "string",
            "const": "quote_line_item"
          },
          "id": {
            "type": "string"
          },
          "article_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "description": {
            "type": "string"
          },
          "unit_type": {
            "type": "string"
          },
          "quantity": {
            "type": "number",
            "format": "double"
          },
          "unit_price": {
            "type": "number",
            "format": "double"
          },
          "unit_discount_rate": {
            "type": "number",
            "format": "double"
          },
          "tax_rate": {
            "type": "number",
            "format": "double"
          },
          "vat_code": {
            "type": [
              "string",
              "null"
            ]
          },
          "total_price": {
            "type": "number",
            "format": "double"
          },
          "total_before_tax": {
            "type": "number",
            "format": "double"
          },
          "total_tax": {
            "type": "number",
            "format": "double"
          },
          "total_after_tax": {
            "type": "number",
            "format": "double"
          },
          "position": {
            "type": "integer"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "Invoice": {
        "type": "object",
        "required": [
          "object",
          "id",
          "status",
          "customer_id",
          "seq_number",
          "payment_method",
          "payment_condition",
          "payment_due_at",
          "vat_exigibility",
          "total_before_tax",
          "total_tax",
          "total_after_tax",
          "total_discount",
          "total_after_discount",
          "total_payments",
          "outstanding_payments",
          "customer_name",
          "created_by_full_name",
          "sdi_status",
          "created_at",
          "updated_at"
        ],
        "description": "A workshop invoice. Multi-tenant: belongs to one company.\nCreated from a worksheet; transitions draft → issued via\n`POST /v1/invoices/{id}/issue`.\n",
        "properties": {
          "object": {
            "type": "string",
            "const": "invoice"
          },
          "id": {
            "type": "string",
            "example": "4412e7b9-0c35-4a18-92d7-5b6ea1f38c04"
          },
          "status": {
            "type": "string",
            "enum": [
              "draft",
              "issued"
            ],
            "description": "Derived from `issued_at`. Soft-deleted invoices 404\nbefore reaching the client — \"deleted\" is never emitted.\n"
          },
          "title": {
            "type": [
              "string",
              "null"
            ]
          },
          "comments": {
            "type": [
              "string",
              "null"
            ]
          },
          "internal_comments": {
            "type": [
              "string",
              "null"
            ]
          },
          "payment_method": {
            "type": "string"
          },
          "payment_condition": {
            "type": "string"
          },
          "payment_due_at": {
            "type": "string",
            "format": "date-time"
          },
          "vat_exigibility": {
            "type": "string"
          },
          "sezionale_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "pa_document_type": {
            "type": [
              "string",
              "null"
            ]
          },
          "pa_document_number": {
            "type": [
              "string",
              "null"
            ]
          },
          "pa_document_date": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "pa_cig": {
            "type": [
              "string",
              "null"
            ]
          },
          "pa_cup": {
            "type": [
              "string",
              "null"
            ]
          },
          "customer_id": {
            "type": "string"
          },
          "vehicle_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "worksheet_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "seq_number": {
            "type": "integer",
            "description": "Per-company monotonic invoice number. 0 on drafts;\nassigned atomically at issuance.\n"
          },
          "total_before_tax": {
            "type": "number",
            "format": "double",
            "description": "Money. Float64 — clients render via the workshop locale\nformatter; avoid arithmetic.\n"
          },
          "total_tax": {
            "type": "number",
            "format": "double"
          },
          "total_after_tax": {
            "type": "number",
            "format": "double"
          },
          "total_discount": {
            "type": "number",
            "format": "double"
          },
          "total_after_discount": {
            "type": "number",
            "format": "double"
          },
          "total_payments": {
            "type": "number",
            "format": "double"
          },
          "outstanding_payments": {
            "type": "number",
            "format": "double"
          },
          "customer_name": {
            "type": "string"
          },
          "vehicle_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "vehicle_license": {
            "type": [
              "string",
              "null"
            ]
          },
          "created_by_full_name": {
            "type": "string"
          },
          "sezionale_code": {
            "type": [
              "string",
              "null"
            ]
          },
          "work_category_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "sdi_status": {
            "type": "string",
            "description": "Italian SDI e-invoice lifecycle status. Empty string\nwhen no e-invoice exists for this invoice. Possible\nnon-empty values: \"sent\", \"rejected\", \"pending_normal\",\n\"pending_warning\", \"pending_late\".\n"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          },
          "issued_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "Populated once the invoice is issued."
          },
          "deleted_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "Soft-deletion timestamp. Populated only on the response\nfrom DELETE /v1/invoices/{id}.\n"
          },
          "line_items": {
            "type": "array",
            "description": "Populated on detail and post-mutation responses. Omitted\nfrom list rows to keep payloads small.\n",
            "items": {
              "$ref": "#/components/schemas/InvoiceLineItem"
            }
          }
        }
      },
      "InvoiceLineItem": {
        "type": "object",
        "required": [
          "object",
          "id",
          "description",
          "quantity",
          "unit_type",
          "unit_price",
          "unit_discount_rate",
          "tax_rate",
          "total_price",
          "total_before_tax",
          "total_tax",
          "total_after_tax",
          "created_at"
        ],
        "description": "A single row on an invoice. Read-only via this API — line\nitem add/remove is a separate concern not yet exposed on\nthe mobile surface.\n",
        "properties": {
          "object": {
            "type": "string",
            "const": "invoice_line_item"
          },
          "id": {
            "type": "string"
          },
          "article_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "description": {
            "type": "string"
          },
          "quantity": {
            "type": "number",
            "format": "double"
          },
          "unit_type": {
            "type": "string"
          },
          "unit_price": {
            "type": "number",
            "format": "double"
          },
          "unit_cost": {
            "type": [
              "number",
              "null"
            ],
            "format": "double"
          },
          "unit_discount_rate": {
            "type": "number",
            "format": "double"
          },
          "tax_rate": {
            "type": "number",
            "format": "double"
          },
          "vat_code": {
            "type": [
              "string",
              "null"
            ]
          },
          "vat_description": {
            "type": [
              "string",
              "null"
            ]
          },
          "total_price": {
            "type": "number",
            "format": "double"
          },
          "total_before_tax": {
            "type": "number",
            "format": "double"
          },
          "total_tax": {
            "type": "number",
            "format": "double"
          },
          "total_after_tax": {
            "type": "number",
            "format": "double"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "CreditNote": {
        "type": "object",
        "required": [
          "object",
          "id",
          "invoice_id",
          "customer_name",
          "vehicle_name",
          "vehicle_license",
          "sdi_status",
          "latest_sdi_status",
          "total_before_tax",
          "total_tax",
          "total_after_tax",
          "created_at",
          "updated_at"
        ],
        "description": "A credit note (nota di credito) — a derivative document\ncreated from a source invoice. Multi-tenant: belongs to one\ncompany. Totals mirror the source invoice and are not\nclient-editable. The only mutable field is `title`.\n",
        "properties": {
          "object": {
            "type": "string",
            "const": "credit_note"
          },
          "id": {
            "type": "string",
            "example": "cnt_4412"
          },
          "invoice_id": {
            "type": "string",
            "description": "Source invoice UUID. Immutable."
          },
          "seq_number": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Per-company / per-sezionale monotonic number. May be\nnull on freshly created credit notes that have not yet\nbeen issued — clients render \"Bozza\" / draft when\nabsent.\n"
          },
          "title": {
            "type": [
              "string",
              "null"
            ]
          },
          "sezionale_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "sezionale_code": {
            "type": [
              "string",
              "null"
            ],
            "description": "Denormalized sezionale code for display. Read-only;\nmutate via `sezionale_id` at creation time.\n"
          },
          "customer_name": {
            "type": "string",
            "description": "Denormalized display string of the source invoice's\ncustomer.\n"
          },
          "vehicle_name": {
            "type": "string",
            "description": "Denormalized display string of the linked vehicle."
          },
          "vehicle_license": {
            "type": "string"
          },
          "sdi_status": {
            "type": "string",
            "description": "Italian SDI e-invoice lifecycle status. Empty string\nwhen no e-credit-note exists.\n"
          },
          "latest_sdi_status": {
            "type": "string",
            "description": "Most recent terminal SDI status observed for this\ncredit note's electronic counterpart.\n"
          },
          "total_before_tax": {
            "type": "number",
            "format": "double"
          },
          "total_tax": {
            "type": "number",
            "format": "double"
          },
          "total_after_tax": {
            "type": "number",
            "format": "double"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          },
          "deleted_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "Soft-deletion timestamp. Populated on the response from\nDELETE /v1/credit-notes/{id}. Credit notes with a\nnon-null deleted_at are not returned by list/detail\nendpoints — this field appears for the delete response\nonly.\n"
          }
        }
      },
      "Checkin": {
        "type": "object",
        "required": [
          "object",
          "id",
          "seq_number",
          "status",
          "customer_id",
          "vehicle_id",
          "customer_name",
          "vehicle_name",
          "created_by",
          "created_at",
          "updated_at"
        ],
        "description": "A vehicle intake / inspection snapshot. Belongs to one\ncompany, one customer, and one vehicle. List responses omit\n`line_items` to keep payloads small; detail responses\npopulate it.\n",
        "properties": {
          "object": {
            "type": "string",
            "const": "checkin"
          },
          "id": {
            "type": "string",
            "example": "chk_4412"
          },
          "seq_number": {
            "type": "integer",
            "description": "Per-company human-readable sequence (e.g. \"Checkin #142\").\nAssigned at create time and stable afterwards.\n"
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "completed"
            ],
            "description": "Lifecycle state. Mutated only through\n`POST /v1/checkins/{id}/transitions`, never via PATCH.\n"
          },
          "title": {
            "type": [
              "string",
              "null"
            ]
          },
          "comments": {
            "type": [
              "string",
              "null"
            ]
          },
          "internal_comments": {
            "type": [
              "string",
              "null"
            ],
            "description": "Notes visible only to workshop staff."
          },
          "customer_id": {
            "type": "string"
          },
          "vehicle_id": {
            "type": "string"
          },
          "work_category_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "worksheet_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "Set when the checkin has been converted into a worksheet."
          },
          "quote_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "Set when the checkin has been converted into a quote."
          },
          "fuel_level": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Free-form integer captured at intake. Convention is\n0–100 (percentage) but the API enforces no bounds today.\n"
          },
          "section1": {
            "type": [
              "string",
              "null"
            ]
          },
          "section2": {
            "type": [
              "string",
              "null"
            ]
          },
          "section3": {
            "type": [
              "string",
              "null"
            ]
          },
          "section4": {
            "type": [
              "string",
              "null"
            ]
          },
          "section5": {
            "type": [
              "string",
              "null"
            ]
          },
          "section6": {
            "type": [
              "string",
              "null"
            ]
          },
          "section7": {
            "type": [
              "string",
              "null"
            ]
          },
          "section8": {
            "type": [
              "string",
              "null"
            ]
          },
          "km": {
            "type": [
              "number",
              "null"
            ],
            "description": "Odometer reading at intake."
          },
          "created_by": {
            "type": "string",
            "description": "User id of the staff member who created the checkin."
          },
          "customer_name": {
            "type": "string",
            "description": "Denormalized display string of the linked customer.\nPre-computed on the entity for list-row rendering\nwithout a second request.\n"
          },
          "vehicle_name": {
            "type": "string",
            "description": "Denormalized display string of the linked vehicle."
          },
          "vehicle_license": {
            "type": [
              "string",
              "null"
            ]
          },
          "work_category_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "delivered_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          },
          "deleted_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "Soft-deletion timestamp. Populated on the response from\nDELETE /v1/checkins/{id}. Checkins with a non-null\ndeleted_at are not returned by list/detail endpoints —\nthis field appears for the delete response only.\n"
          },
          "line_items": {
            "type": [
              "array",
              "null"
            ],
            "description": "Embedded line items (description-only notes). Populated on\ndetail / batch responses; list GET emits `null` to keep\npayloads small. Mutate via\nPOST /v1/checkins/{id}/line-items/batch.\n",
            "items": {
              "$ref": "#/components/schemas/CheckinLineItem"
            }
          }
        }
      },
      "CheckinLineItem": {
        "type": "object",
        "required": [
          "object",
          "id",
          "description",
          "created_at"
        ],
        "description": "A description-only line item (note) on a checkin. Returned\nread-only on checkin responses; mutate via\nPOST /v1/checkins/{id}/line-items/batch.\n",
        "properties": {
          "object": {
            "type": "string",
            "const": "checkin_line_item"
          },
          "id": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "CalendarEntry": {
        "type": "object",
        "required": [
          "object",
          "id",
          "title",
          "user_id",
          "start_time",
          "end_time",
          "color",
          "email_reminder",
          "auto_reminder_enabled",
          "created_at",
          "updated_at"
        ],
        "description": "A workshop appointment. Belongs to one company (the\ncaller's) and is owned by the user who created it. May\nreference an optional Customer, Vehicle, scheda lavoro\n(`worksheet_id`), work category, and parent Calendar. While\n`worksheet_id` is set, `customer_id` is the worksheet's and so\nis `vehicle_id` whenever the worksheet has one.\n",
        "properties": {
          "object": {
            "type": "string",
            "const": "calendar_entry"
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "title": {
            "type": "string"
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "user_id": {
            "type": "string",
            "format": "uuid"
          },
          "start_time": {
            "type": "string",
            "format": "date-time"
          },
          "end_time": {
            "type": "string",
            "format": "date-time"
          },
          "color": {
            "type": "string",
            "description": "Hex colour, e.g. #2563eb"
          },
          "email_reminder": {
            "type": "boolean"
          },
          "customer_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "vehicle_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "worksheet_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "quote_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "checkin_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "work_category_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "work_category_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "calendar_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "auto_reminder_enabled": {
            "type": "boolean"
          },
          "auto_reminder_channel": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "email",
              "sms",
              "whatsapp",
              null
            ]
          },
          "auto_reminder_sent_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "customer": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/CalendarEntryCustomer"
              },
              {
                "type": "null"
              }
            ]
          },
          "vehicle": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/CalendarEntryVehicle"
              },
              {
                "type": "null"
              }
            ]
          },
          "user": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/CalendarEntryUser"
              },
              {
                "type": "null"
              }
            ]
          },
          "calendar": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/CalendarEntryCalendar"
              },
              {
                "type": "null"
              }
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          },
          "deleted_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "CalendarEntryCustomer": {
        "type": "object",
        "required": [
          "object",
          "id"
        ],
        "description": "Slim denormalized view of the linked customer, embedded\ninside a CalendarEntry. The full Customer resource lives at\n`/v1/customers/{id}`.\n",
        "properties": {
          "object": {
            "type": "string",
            "const": "customer"
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "full_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "email": {
            "type": [
              "string",
              "null"
            ]
          },
          "phone": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "CalendarEntryVehicle": {
        "type": "object",
        "required": [
          "object",
          "id",
          "model",
          "manufacturer_name"
        ],
        "description": "Slim denormalized view of the linked vehicle, embedded\ninside a CalendarEntry. The full Vehicle resource lives at\n`/v1/vehicles/{id}`.\n",
        "properties": {
          "object": {
            "type": "string",
            "const": "vehicle"
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "license": {
            "type": [
              "string",
              "null"
            ]
          },
          "model": {
            "type": "string"
          },
          "manufacturer_name": {
            "type": "string"
          }
        }
      },
      "CalendarEntryUser": {
        "type": "object",
        "required": [
          "object",
          "id",
          "name"
        ],
        "description": "Slim denormalized view of the owning user, embedded inside a\nCalendarEntry.\n",
        "properties": {
          "object": {
            "type": "string",
            "const": "user"
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          }
        }
      },
      "CalendarEntryCalendar": {
        "type": "object",
        "required": [
          "object",
          "id",
          "name"
        ],
        "description": "Slim denormalized view of the parent calendar, embedded\ninside a CalendarEntry. The Calendar container itself is\nnot exposed on this tier.\n",
        "properties": {
          "object": {
            "type": "string",
            "const": "calendar"
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          }
        }
      },
      "CashBookEntry": {
        "type": "object",
        "required": [
          "object",
          "id",
          "payment_type",
          "amount",
          "payment_date",
          "financial_account_name",
          "running_balance"
        ],
        "description": "One row of the cashbook: either an income (linked to an\ninvoice or worksheet) or an expense (linked to a supplier\nbill). Computed from underlying payments — no first-class\nlifecycle.\n",
        "properties": {
          "object": {
            "type": "string",
            "const": "cashbook_entry"
          },
          "id": {
            "type": "string",
            "description": "Underlying payment UUID. Opaque to clients."
          },
          "payment_type": {
            "type": "string",
            "enum": [
              "income",
              "expense"
            ],
            "description": "Side of the ledger. Clients branch on this to decide\nwhich side-specific fields (invoice_/worksheet_ vs\nexpense_, customer_name vs supplier_name) are populated.\n"
          },
          "amount": {
            "type": "number",
            "format": "double",
            "description": "Signed payment amount in the workshop's currency.\nFloat64 matches the entity layer; the cashbook computes\na running balance, so integer cents would force clients\nto redo the math.\n"
          },
          "payment_date": {
            "type": "string",
            "format": "date-time",
            "description": "When the payment was recorded. RFC 3339 UTC."
          },
          "financial_account_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "Account this payment hit. Nullable — legacy entries may\npredate the financial-accounts feature.\n"
          },
          "financial_account_name": {
            "type": "string",
            "description": "Denormalized account name. Empty string for legacy\nentries with no account assignment.\n"
          },
          "customer_name": {
            "type": "string",
            "description": "Income-side counterparty. Omitted from the JSON entirely\non expense rows (no empty key).\n"
          },
          "supplier_name": {
            "type": "string",
            "description": "Expense-side counterparty. Omitted from the JSON\nentirely on income rows.\n"
          },
          "invoice_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "Source invoice UUID (income rows)."
          },
          "invoice_seq_number": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Sequence number of the linked issued invoice, if any.\nSet on income rows backed by an issued invoice.\n"
          },
          "invoice_issued_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "worksheet_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "Source worksheet UUID, set on income rows backed by a\nworksheet whose invoice is still a draft.\n"
          },
          "worksheet_seq_number": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Sequence number of the linked worksheet, set on income\nrows backed by a worksheet whose invoice is still a draft.\n"
          },
          "worksheet_created_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "receipt_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "Source receipt (scontrino) UUID (income rows)."
          },
          "receipt_seq_number": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Sequence number of the linked standalone scontrino."
          },
          "receipt_issued_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "expense_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "Source expense UUID (expense rows)."
          },
          "expense_seq_number": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Sequence number of the linked supplier expense."
          },
          "expense_created_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "manual_entry_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "Set on a manual prima nota movement — a row typed straight\ninto the ledger with no source document, so every other\n`*_id` above is null. Its value is the id used to edit or\ndelete the movement on the web app.\n"
          },
          "manual_description": {
            "type": "string",
            "description": "Free-text label of a manual movement, shown in place of the\ndocument reference other rows carry. Empty on every other row.\n"
          },
          "manual_payment_method": {
            "type": "string",
            "description": "Payment method code (MP01, MP05, …) of a manual movement.\nEmpty on every other row.\n"
          },
          "running_balance": {
            "type": "number",
            "format": "double",
            "description": "Cumulative balance after applying this row, starting from\n`opening_balance` on the list envelope. Computed by the\nserver in `order_direction` — do not re-sort client-side\nwithout recomputing.\n"
          }
        }
      },
      "DateFilter": {
        "type": "object",
        "required": [
          "object",
          "preset"
        ],
        "description": "The effective date range the server applied to a date-scoped\nlist request, echoed on the list envelope so clients can render\nthe active filter without re-deriving it.\n\nA request that sends no `from`/`to`/`preset` gets the default\nwindow, the last 12 months (`rel365`); nothing is carried over\nbetween requests. Relative presets are resolved here to concrete\ndates and roll forward over time.\n",
        "properties": {
          "object": {
            "type": "string",
            "const": "date_filter"
          },
          "preset": {
            "type": "string",
            "description": "Effective selection label. Relative (`rel30`/`rel90`/`rel365`)\nand absolute (`year`/`Q1`-`Q4`/`month`/`custom`) carry\n`from`/`to`; `all` omits them.\n",
            "enum": [
              "rel30",
              "rel90",
              "rel365",
              "year",
              "Q1",
              "Q2",
              "Q3",
              "Q4",
              "month",
              "custom",
              "all"
            ]
          },
          "from": {
            "type": "string",
            "format": "date",
            "description": "Inclusive lower bound (YYYY-MM-DD). Omitted when `preset` is `all`."
          },
          "to": {
            "type": "string",
            "format": "date",
            "description": "Inclusive upper bound (YYYY-MM-DD). Omitted when `preset` is `all`."
          }
        }
      },
      "CalendarFilter": {
        "type": "object",
        "required": [
          "object",
          "calendar_id"
        ],
        "description": "The effective calendar selection the server applied to a calendar-entry\ntimeframe listing, echoed on the list envelope so clients can render the\nactive calendar without re-deriving it.\n\nAn empty `calendar_id` means \"all calendars\"; a selection is not\ncarried over between requests.\nEchoed only on the default timeframe listing — `customer_id` /\n`vehicle_id` lookups don't apply a calendar filter.\n",
        "properties": {
          "object": {
            "type": "string",
            "const": "calendar_filter"
          },
          "calendar_id": {
            "type": "string",
            "description": "Selected parent calendar id, or empty for all calendars."
          }
        }
      },
      "ListEnvelope": {
        "type": "object",
        "required": [
          "object",
          "data",
          "has_more",
          "url"
        ],
        "properties": {
          "object": {
            "type": "string",
            "const": "list",
            "description": "Polymorphic discriminator."
          },
          "data": {
            "type": "array",
            "description": "Page of items. Each item carries its own `object` field.",
            "items": {}
          },
          "has_more": {
            "type": "boolean",
            "description": "True if more results exist beyond this page. Use this\nfor the \"load more\" UI — `data.length` alone is\nunreliable because a full page may still be the last\npage.\n"
          },
          "next_starting_after": {
            "type": "string",
            "description": "Opaque cursor for the next page. Present (and non-empty)\nwhen `has_more` is `true`; omitted otherwise. Pass the\nvalue verbatim as `?starting_after=` on the follow-up\nrequest. Clients MUST NOT parse it — today it encodes\nan offset, tomorrow it may encode a `(created_at, id)`\ntuple without a wire-visible change.\n"
          },
          "url": {
            "type": "string",
            "description": "The request path that produced this list."
          }
        }
      },
      "LicensePlateLookup": {
        "type": "object",
        "required": [
          "object",
          "license_plate",
          "manufacturer_id",
          "manufacturer_name",
          "model",
          "variant",
          "vin",
          "engine_category",
          "engine_power_kw",
          "engine_displacement_ccm",
          "registered_at",
          "comments",
          "credits_spent",
          "credit_balance"
        ],
        "description": "Visura targa result, pre-mapped onto the vehicle create-form\nfields. A successful lookup has already deducted\n`credits_spent` from the balance.\n",
        "properties": {
          "object": {
            "type": "string",
            "const": "license_plate_lookup"
          },
          "license_plate": {
            "type": "string"
          },
          "manufacturer_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "Fuzzy-matched company manufacturer; null when none matched."
          },
          "manufacturer_name": {
            "type": "string",
            "description": "The raw make from the database."
          },
          "model": {
            "type": "string"
          },
          "variant": {
            "type": "string"
          },
          "vin": {
            "type": "string"
          },
          "engine_category": {
            "type": "string",
            "description": "Internal fuel enum (diesel, gasoline, …); empty when unknown."
          },
          "engine_power_kw": {
            "type": [
              "integer",
              "null"
            ]
          },
          "engine_displacement_ccm": {
            "type": [
              "integer",
              "null"
            ]
          },
          "registered_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "comments": {
            "type": "string",
            "description": "The \"Dati da banca dati\" notes block, newline-separated."
          },
          "credits_spent": {
            "type": "number"
          },
          "credit_balance": {
            "type": "number"
          }
        }
      },
      "WorksheetFiscalDocument": {
        "type": "object",
        "required": [
          "object",
          "id",
          "seq_number",
          "issued_at",
          "created_at",
          "total_after_discount",
          "outstanding_payments"
        ],
        "description": "One compact linked fiscal document on a worksheet detail —\nseq_number 0 means a draft (\"Non emessa\" / \"Bozza\").\n",
        "properties": {
          "object": {
            "type": "string",
            "const": "worksheet_fiscal_document"
          },
          "id": {
            "type": "string"
          },
          "seq_number": {
            "type": "integer"
          },
          "issued_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "total_after_discount": {
            "type": "number",
            "format": "double"
          },
          "outstanding_payments": {
            "type": "number",
            "format": "double"
          }
        }
      },
      "CashBookListEnvelope": {
        "description": "Extends `ListEnvelope` with the two fields the cashbook\nneeds to be self-consistent: `opening_balance` (the\nrunning-balance anchor) and `total_items` (the full\nfiltered count). The envelope's `{object, data, has_more,\nurl}` shape stays wire-compatible with the generic list\nparser; the extra fields are inert to clients that don't\nknow about them.\n",
        "allOf": [
          {
            "$ref": "#/components/schemas/ListEnvelope"
          },
          {
            "type": "object",
            "required": [
              "opening_balance",
              "total_items"
            ],
            "properties": {
              "data": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/CashBookEntry"
                }
              },
              "opening_balance": {
                "type": "number",
                "format": "double",
                "description": "Running-balance anchor at the start of the `from`\nwindow. Zero when `from` is unset.\n"
              },
              "total_items": {
                "type": "integer",
                "description": "Full filtered count, ignoring pagination."
              },
              "date_filter": {
                "$ref": "#/components/schemas/DateFilter"
              }
            }
          }
        ]
      },
      "ErrorEnvelope": {
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "$ref": "#/components/schemas/ErrorBody"
          }
        }
      },
      "ErrorBody": {
        "type": "object",
        "required": [
          "type",
          "status",
          "code",
          "message"
        ],
        "description": "Single error per response (fast-fail). Inspired by RFC 9457\nProblem Details, wrapped in an `error` root key so success\nand error bodies are visually distinguishable.\n",
        "properties": {
          "type": {
            "type": "string",
            "format": "uri",
            "description": "URI identifying the error class. Stable.",
            "example": "https://api.officina.it/errors/invalid_credentials"
          },
          "status": {
            "type": "integer",
            "description": "HTTP status code, mirrored from the response.",
            "example": 401
          },
          "code": {
            "type": "string",
            "description": "Stable machine-readable identifier (snake_case). Clients\nbranch on this, never on `message`.\n",
            "example": "invalid_credentials"
          },
          "message": {
            "type": "string",
            "description": "Human-readable English explanation."
          },
          "param": {
            "type": "string",
            "description": "JSON Pointer (RFC 6901) to the offending request field.\nOmitted when the error isn't tied to a specific input\nfield (e.g. auth errors).\n",
            "example": "/email"
          },
          "trace_id": {
            "type": "string",
            "description": "Request trace ID for support correlation."
          }
        }
      },
      "ArticleCreateRequest": {
        "type": "object",
        "description": "ArticleCreateRequest / ArticlePatchRequest — the magazzino and\nlistino manodopera editors. unit_type decides the kind.\n",
        "required": [
          "name",
          "code",
          "unit_type",
          "stock",
          "minimum_stock",
          "purchase_price",
          "sale_price",
          "vat_code",
          "unit_discount_rate",
          "location",
          "comments",
          "supplier_id"
        ],
        "properties": {
          "name": {
            "type": "string"
          },
          "code": {
            "type": [
              "string",
              "null"
            ]
          },
          "unit_type": {
            "type": "string"
          },
          "stock": {
            "type": [
              "number",
              "null"
            ]
          },
          "minimum_stock": {
            "type": [
              "number",
              "null"
            ]
          },
          "purchase_price": {
            "type": [
              "number",
              "null"
            ]
          },
          "sale_price": {
            "type": [
              "number",
              "null"
            ]
          },
          "vat_code": {
            "type": "string"
          },
          "unit_discount_rate": {
            "type": [
              "integer",
              "null"
            ]
          },
          "location": {
            "type": [
              "string",
              "null"
            ]
          },
          "comments": {
            "type": [
              "string",
              "null"
            ]
          },
          "supplier_id": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "ArticleMovement": {
        "type": "object",
        "description": "ArticleMovement is one \"Storico lavorazioni\" row for an article /\nlavorazione — a sale movement where the item was used. Exactly one\nsource (worksheet / order / receipt) is set; SourceID is the id\nthe mobile client navigates to.\n",
        "required": [
          "object",
          "id",
          "quantity",
          "source",
          "source_id",
          "seq_number",
          "document_date",
          "customer_name",
          "vehicle_name",
          "total",
          "created_at"
        ],
        "properties": {
          "object": {
            "type": "string",
            "description": "always \"article_movement\""
          },
          "id": {
            "type": "string"
          },
          "quantity": {
            "type": "number"
          },
          "source": {
            "type": "string",
            "description": "\"worksheet\" | \"order\" | \"receipt\" | \"\""
          },
          "source_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "seq_number": {
            "type": [
              "integer",
              "null"
            ]
          },
          "document_date": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "customer_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "vehicle_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "total": {
            "type": [
              "number",
              "null"
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "CalendarListEnvelope": {
        "type": "object",
        "description": "CalendarListEnvelope: success body for GET /v1/calendars. Follows\nthe standard list envelope; the collection is unpaginated\n(workshops have a handful of calendars at most) so HasMore is\nalways false.\n",
        "required": [
          "object",
          "data",
          "has_more",
          "url"
        ],
        "properties": {
          "object": {
            "type": "string",
            "description": "always \"list\""
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Calendar"
            }
          },
          "has_more": {
            "type": "boolean"
          },
          "url": {
            "type": "string"
          }
        }
      },
      "CheckinDocument": {
        "type": "object",
        "description": "CheckinDocument: one attachment on a checkin (Documenti Allegati).\nPopulated on detail responses only. URL points at the\nauthenticated download route.\n",
        "required": [
          "object",
          "id",
          "name",
          "content_type",
          "description",
          "url",
          "file_size_bytes",
          "created_at"
        ],
        "properties": {
          "object": {
            "type": "string",
            "description": "always \"checkin_document\""
          },
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "content_type": {
            "type": "string"
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "url": {
            "type": "string"
          },
          "file_size_bytes": {
            "type": "integer"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "CheckinStatusCounts": {
        "type": "object",
        "description": "CheckinStatusCounts: tab counters for the checkins list (web\ncheckin-list StatusCount parity), computed with the same q + date\nfilters as the list.\n",
        "required": [
          "object",
          "active",
          "completed",
          "date_filter"
        ],
        "properties": {
          "object": {
            "type": "string",
            "description": "always \"checkin_status_counts\""
          },
          "active": {
            "type": "integer"
          },
          "completed": {
            "type": "integer"
          },
          "date_filter": {
            "$ref": "#/components/schemas/DateFilter"
          }
        }
      },
      "CompanyPatchRequest": {
        "type": "object",
        "description": "Body of `PATCH /v1/company`. Three-state semantics\n(omitted = leave unchanged, explicit `null` = clear, concrete\nvalue = set) on the nullable fields. The\nnon-nullable ones — `name`, `entity_type`, `checkin_frame`,\n`document_template_name`, `default_payment_method`,\n`default_payment_due_at` and the booleans — ignore an explicit\n`null` rather than clearing.\n\n`name`, `entity_type`, `tax_id` and `fiscal_code` are rejected\nwith 409 while electronic invoicing is configured\n(`fiscal_fields_locked` in the response).\n\nEverything else about the officina — logo, sezionali, users — is\nnot patchable here.\n",
        "properties": {
          "name": {
            "type": "string"
          },
          "entity_type": {
            "type": "string",
            "enum": [
              "company",
              "individual"
            ]
          },
          "company_type": {
            "type": [
              "string",
              "null"
            ],
            "description": "car_repair, tyre_repair, …"
          },
          "tax_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "Partita IVA — 11 digits."
          },
          "fiscal_code": {
            "type": [
              "string",
              "null"
            ]
          },
          "email": {
            "type": [
              "string",
              "null"
            ]
          },
          "phone": {
            "type": [
              "string",
              "null"
            ],
            "description": "Stored as +39…; a national number is normalised."
          },
          "address": {
            "type": [
              "string",
              "null"
            ]
          },
          "city": {
            "type": [
              "string",
              "null"
            ]
          },
          "zip_code": {
            "type": [
              "string",
              "null"
            ]
          },
          "province": {
            "type": [
              "string",
              "null"
            ],
            "description": "Inferred from the CAP when left empty."
          },
          "checkin_frame": {
            "type": "string",
            "enum": [
              "auto",
              "moto"
            ],
            "description": "The outline used on the checkin damage diagram."
          },
          "primary_color": {
            "type": [
              "string",
              "null"
            ]
          },
          "document_template_name": {
            "type": "string"
          },
          "default_payment_method": {
            "type": "string"
          },
          "default_payment_due_at": {
            "type": "string"
          },
          "bank_account_iban": {
            "type": [
              "string",
              "null"
            ]
          },
          "bank_account_holder": {
            "type": [
              "string",
              "null"
            ]
          },
          "quote_hide_line_item_details": {
            "type": "boolean"
          },
          "worksheet_hide_line_item_details": {
            "type": "boolean"
          },
          "worksheet_line_item_code_enabled": {
            "type": "boolean"
          },
          "line_items_including_vat": {
            "type": "boolean"
          },
          "worksheet_km_required": {
            "type": "boolean"
          }
        },
        "additionalProperties": false
      },
      "CompanyProfile": {
        "type": "object",
        "description": "CompanyProfile — Informazioni officina (anagrafica scope).\n",
        "required": [
          "object",
          "id",
          "name",
          "entity_type",
          "company_type",
          "tax_id",
          "fiscal_code",
          "email",
          "phone",
          "phone_national",
          "address",
          "city",
          "zip_code",
          "province",
          "fiscal_fields_locked",
          "checkin_frame",
          "primary_color",
          "document_template_name",
          "default_payment_method",
          "default_payment_due_at",
          "bank_account_iban",
          "bank_account_holder",
          "quote_hide_line_item_details",
          "worksheet_hide_line_item_details",
          "worksheet_line_item_code_enabled",
          "line_items_including_vat",
          "worksheet_km_required"
        ],
        "properties": {
          "object": {
            "type": "string",
            "description": "always \"company\""
          },
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "entity_type": {
            "type": "string",
            "description": "company | individual"
          },
          "company_type": {
            "type": [
              "string",
              "null"
            ]
          },
          "tax_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "fiscal_code": {
            "type": [
              "string",
              "null"
            ]
          },
          "email": {
            "type": [
              "string",
              "null"
            ]
          },
          "phone": {
            "type": [
              "string",
              "null"
            ]
          },
          "phone_national": {
            "type": [
              "string",
              "null"
            ],
            "description": "The phone number without the +39 country code."
          },
          "address": {
            "type": [
              "string",
              "null"
            ]
          },
          "city": {
            "type": [
              "string",
              "null"
            ]
          },
          "zip_code": {
            "type": [
              "string",
              "null"
            ]
          },
          "province": {
            "type": [
              "string",
              "null"
            ]
          },
          "fiscal_fields_locked": {
            "type": "boolean",
            "description": "True when electronic-invoicing registration locks the fiscal fields."
          },
          "checkin_frame": {
            "type": "string",
            "description": "\"auto\" | \"moto\" — the outline used on the checkin damage diagram."
          },
          "primary_color": {
            "type": [
              "string",
              "null"
            ]
          },
          "document_template_name": {
            "type": "string"
          },
          "default_payment_method": {
            "type": "string"
          },
          "default_payment_due_at": {
            "type": "string"
          },
          "bank_account_iban": {
            "type": [
              "string",
              "null"
            ]
          },
          "bank_account_holder": {
            "type": [
              "string",
              "null"
            ]
          },
          "quote_hide_line_item_details": {
            "type": "boolean"
          },
          "worksheet_hide_line_item_details": {
            "type": "boolean"
          },
          "worksheet_line_item_code_enabled": {
            "type": "boolean"
          },
          "line_items_including_vat": {
            "type": "boolean"
          },
          "worksheet_km_required": {
            "type": "boolean"
          }
        }
      },
      "CustomerMessageEntity": {
        "type": "object",
        "description": "CustomerMessageEntity — one eligible target of the Invia messaggio\nflow (web customer-{channel}-entity-list parity).\n",
        "required": [
          "object",
          "id",
          "seq_number",
          "title",
          "vehicle_name",
          "vehicle_license",
          "created_at",
          "total"
        ],
        "properties": {
          "object": {
            "type": "string",
            "description": "always \"customer_message_entity\""
          },
          "id": {
            "type": "string"
          },
          "seq_number": {
            "type": "integer"
          },
          "title": {
            "type": "string",
            "description": "calendar entries use the title"
          },
          "vehicle_name": {
            "type": "string"
          },
          "vehicle_license": {
            "type": "string"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "total": {
            "type": "number"
          }
        }
      },
      "CustomerMessageSendRequest": {
        "type": "object",
        "description": "CustomerMessageSendRequest — POST /v1/customers/{id}/messages.\n",
        "required": [
          "type",
          "entity_id",
          "channel",
          "recipient",
          "subject",
          "message",
          "sender_method"
        ],
        "properties": {
          "type": {
            "type": "string",
            "description": "worksheet | invoice | quote | calendar | revisione | service | freetext"
          },
          "entity_id": {
            "type": "string"
          },
          "channel": {
            "type": "string",
            "description": "email | sms | whatsapp"
          },
          "recipient": {
            "type": "string"
          },
          "subject": {
            "type": "string"
          },
          "message": {
            "type": "string"
          },
          "sender_method": {
            "type": "string"
          }
        }
      },
      "DeliveryNote": {
        "type": "object",
        "description": "DeliveryNote is one \"Documenti di trasporto\" (DDT) row. The list\n(GET /v1/delivery-notes) mirrors the web delivery-note-list: the\nsupplier (or the causale/description when no supplier), the\nnumber, the delivery date and the amount. status is \"draft\"\n(Bozza) or \"issued\" (Emesso).\n",
        "required": [
          "object",
          "id",
          "seq_number",
          "supplier_id",
          "supplier_name",
          "description",
          "status",
          "delivery_date",
          "total_before_tax",
          "total_tax",
          "total_after_tax",
          "created_at"
        ],
        "properties": {
          "object": {
            "type": "string",
            "description": "always \"delivery_note\""
          },
          "id": {
            "type": "string"
          },
          "seq_number": {
            "type": "integer"
          },
          "supplier_id": {
            "type": "string"
          },
          "supplier_name": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "status": {
            "type": "string"
          },
          "delivery_date": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "total_before_tax": {
            "type": "number"
          },
          "total_tax": {
            "type": "number"
          },
          "total_after_tax": {
            "type": "number"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "DeliveryNoteStatusCounts": {
        "type": "object",
        "description": "DeliveryNoteStatusCounts backs the DDT status tabs (Bozza /\nEmessi). Counts are scoped to the applied date range (echoed in\ndate_filter), the free-text search and the selected supplier — web\nparity.\n",
        "required": [
          "object",
          "draft",
          "issued",
          "date_filter"
        ],
        "properties": {
          "object": {
            "type": "string",
            "description": "always \"delivery_note_status_counts\""
          },
          "draft": {
            "type": "integer"
          },
          "issued": {
            "type": "integer"
          },
          "date_filter": {
            "$ref": "#/components/schemas/DateFilter"
          }
        }
      },
      "ElectronicInvoice": {
        "type": "object",
        "description": "ElectronicInvoice — one outgoing (attiva) SDI transmission row.\nThe list is a UNION of fatture (TD01), note di credito (TD04) and\nscontrini, so the client routes each row by document_type (see the\nmobile trasmissioni-sdi list): TD01 → the e-invoice detail (this\nID), TD04 → the credit-note detail (CreditNoteID), scontrino → the\nreceipt detail (this ID = receipt id).\n",
        "required": [
          "object",
          "id",
          "status",
          "document_type",
          "invoice_id",
          "credit_note_id",
          "seq_number",
          "customer_name",
          "vehicle_name",
          "total_after_discount",
          "date",
          "sent_at",
          "delivered_at",
          "rejected_at",
          "created_at"
        ],
        "properties": {
          "object": {
            "type": "string",
            "description": "always \"electronic_invoice\""
          },
          "id": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "description": "draft | sent | delivered | rejected | cancelled"
          },
          "document_type": {
            "type": "string",
            "description": "TD01 (fattura) | TD04 (nota di credito) | scontrino"
          },
          "invoice_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "credit_note_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "TD04 rows only: the credit-note detail id"
          },
          "receipt_number": {
            "type": [
              "string",
              "null"
            ],
            "description": "scontrino rows: AdE document number, used as title when no customer"
          },
          "seq_number": {
            "type": "integer"
          },
          "customer_name": {
            "type": "string"
          },
          "vehicle_name": {
            "type": "string"
          },
          "total_after_discount": {
            "type": "number"
          },
          "date": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "sent_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "delivered_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "rejected_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "ElectronicInvoiceIncoming": {
        "type": "object",
        "description": "ElectronicInvoiceIncoming — one passive supplier invoice from\nACube.\n",
        "required": [
          "object",
          "id",
          "supplier_id",
          "business_name",
          "invoice_number",
          "invoice_date",
          "total_amount",
          "status",
          "document_type",
          "expense_id",
          "created_at"
        ],
        "properties": {
          "object": {
            "type": "string",
            "description": "always \"electronic_invoice_incoming\""
          },
          "id": {
            "type": "string"
          },
          "supplier_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "business_name": {
            "type": "string"
          },
          "invoice_number": {
            "type": [
              "string",
              "null"
            ]
          },
          "invoice_date": {
            "type": "string",
            "format": "date-time"
          },
          "total_amount": {
            "type": "number"
          },
          "status": {
            "type": "string",
            "description": "received | quarantena"
          },
          "document_type": {
            "type": "string"
          },
          "expense_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "ElectronicInvoiceIncomingStatusCounts": {
        "type": "object",
        "description": "ElectronicInvoiceIncomingStatusCounts — Passive tab counters.\nEchoes the applied date filter (see\nElectronicInvoiceStatusCounts).\n",
        "required": [
          "object",
          "received",
          "quarantena",
          "date_filter"
        ],
        "properties": {
          "object": {
            "type": "string",
            "description": "always \"electronic_invoice_incoming_status_counts\""
          },
          "received": {
            "type": "integer"
          },
          "quarantena": {
            "type": "integer"
          },
          "date_filter": {
            "$ref": "#/components/schemas/DateFilter"
          }
        }
      },
      "ElectronicInvoiceStatusCounts": {
        "type": "object",
        "description": "ElectronicInvoiceStatusCounts — Attive tab counters. Echoes the\napplied date filter so the client can show the period without a\nprobe.\n",
        "required": [
          "object",
          "draft",
          "sent",
          "delivered",
          "rejected",
          "cancelled",
          "date_filter"
        ],
        "properties": {
          "object": {
            "type": "string",
            "description": "always \"electronic_invoice_status_counts\""
          },
          "draft": {
            "type": "integer"
          },
          "sent": {
            "type": "integer"
          },
          "delivered": {
            "type": "integer"
          },
          "rejected": {
            "type": "integer"
          },
          "cancelled": {
            "type": "integer"
          },
          "date_filter": {
            "$ref": "#/components/schemas/DateFilter"
          }
        }
      },
      "EmailSender": {
        "type": "object",
        "description": "EmailSender: one configured sending identity — the same options\nthe web \"Mittente\" dropdown offers. \"internal\" is always present;\n\"smtp\" and \"gmail\" appear only when configured (in the web\nsettings — configuration itself is not exposed on this API).\n",
        "required": [
          "key",
          "label",
          "default"
        ],
        "properties": {
          "key": {
            "type": "string",
            "description": "internal | smtp | gmail"
          },
          "label": {
            "type": "string",
            "description": "e.g. \"Account Gmail (x@gmail.com)\""
          },
          "default": {
            "type": "boolean"
          }
        }
      },
      "Expense": {
        "type": "object",
        "description": "Expense (spesa) — passive invoices from suppliers.\n",
        "required": [
          "object",
          "id",
          "seq_number",
          "supplier_id",
          "supplier_name",
          "expense_category_id",
          "category_name",
          "description",
          "invoice_number",
          "comments",
          "status",
          "expired",
          "payment_method",
          "payment_due_at",
          "total_before_tax",
          "total_tax",
          "total_after_tax",
          "total_discount",
          "created_by_full_name",
          "created_at",
          "updated_at",
          "deleted_at"
        ],
        "properties": {
          "object": {
            "type": "string",
            "description": "always \"expense\""
          },
          "id": {
            "type": "string"
          },
          "seq_number": {
            "type": "integer"
          },
          "supplier_id": {
            "type": "string"
          },
          "supplier_name": {
            "type": "string"
          },
          "expense_category_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "category_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "invoice_number": {
            "type": [
              "string",
              "null"
            ]
          },
          "comments": {
            "type": [
              "string",
              "null"
            ],
            "description": "\"to_pay\" | \"paid\"; expired = to_pay past the due date."
          },
          "status": {
            "type": "string"
          },
          "expired": {
            "type": "boolean"
          },
          "payment_method": {
            "type": [
              "string",
              "null"
            ]
          },
          "payment_due_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "total_before_tax": {
            "type": "number"
          },
          "total_tax": {
            "type": "number"
          },
          "total_after_tax": {
            "type": "number"
          },
          "total_discount": {
            "type": "number"
          },
          "created_by_full_name": {
            "type": "string"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          },
          "deleted_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "Detail responses only."
          },
          "line_items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ExpenseLineItem"
            }
          },
          "installments": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ExpenseInstallment"
            },
            "description": "Detail responses only — web expense-detail parity. TotalPayments /"
          },
          "total_payments": {
            "type": [
              "number",
              "null"
            ]
          },
          "outstanding_payments": {
            "type": [
              "number",
              "null"
            ]
          },
          "is_stornata": {
            "type": [
              "boolean",
              "null"
            ]
          },
          "schedule": {
            "$ref": "#/components/schemas/ExpenseScheduleInfo"
          },
          "recurring_expenses": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ExpenseRecurringRow"
            }
          },
          "incoming_sdi": {
            "$ref": "#/components/schemas/ExpenseIncomingSDIInfo"
          }
        }
      },
      "ExpenseCategory": {
        "type": "object",
        "description": "ExpenseCategory is one spese category option.\n",
        "required": [
          "object",
          "id",
          "name"
        ],
        "properties": {
          "object": {
            "type": "string",
            "description": "always \"expense_category\""
          },
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          }
        }
      },
      "ExpenseCreateRequest": {
        "type": "object",
        "description": "ExpenseCreateRequest — the simplified mobile create: totals\nentered directly (line items are a web-side refinement).\n",
        "required": [
          "supplier_id",
          "description",
          "invoice_number",
          "expense_category_id",
          "payment_method",
          "payment_due_at",
          "total_before_tax",
          "total_tax",
          "comments"
        ],
        "properties": {
          "supplier_id": {
            "type": "string"
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "invoice_number": {
            "type": [
              "string",
              "null"
            ]
          },
          "expense_category_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "payment_method": {
            "type": [
              "string",
              "null"
            ]
          },
          "payment_due_at": {
            "$ref": "#/components/schemas/WritableTimestampNullable"
          },
          "total_before_tax": {
            "type": "number"
          },
          "total_tax": {
            "type": "number"
          },
          "comments": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "ExpenseIncomingSDIInfo": {
        "type": "object",
        "description": "ExpenseIncomingSDIInfo — the ricezione SDI this expense was\ncreated from.\n",
        "required": [
          "object",
          "id",
          "invoice_number"
        ],
        "properties": {
          "object": {
            "type": "string",
            "description": "always \"electronic_invoice_incoming_ref\""
          },
          "id": {
            "type": "string"
          },
          "invoice_number": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "ExpenseInstallment": {
        "type": "object",
        "description": "ExpenseInstallment is one planned scadenza.\n",
        "required": [
          "object",
          "id",
          "due_date",
          "amount",
          "paid"
        ],
        "properties": {
          "object": {
            "type": "string",
            "description": "always \"expense_installment\""
          },
          "id": {
            "type": "string"
          },
          "due_date": {
            "type": "string",
            "format": "date-time"
          },
          "amount": {
            "type": "number"
          },
          "paid": {
            "type": "boolean"
          }
        }
      },
      "ExpenseLineItem": {
        "type": "object",
        "required": [
          "object",
          "id",
          "description",
          "quantity",
          "unit_type",
          "unit_price",
          "unit_discount_rate",
          "tax_rate",
          "total_price"
        ],
        "properties": {
          "object": {
            "type": "string",
            "description": "always \"expense_line_item\""
          },
          "id": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "quantity": {
            "type": "number"
          },
          "unit_type": {
            "type": "string"
          },
          "unit_price": {
            "type": "number"
          },
          "unit_discount_rate": {
            "type": "number"
          },
          "tax_rate": {
            "type": "number"
          },
          "total_price": {
            "type": "number"
          }
        }
      },
      "ExpensePatchRequest": {
        "type": "object",
        "description": "Body of `PATCH /v1/expenses/{id}`. Three-state semantics\n(omitted = leave unchanged, explicit `null` = clear, concrete\nvalue = set). `total_before_tax` and\n`total_tax` are not nullable and ignore an explicit `null`.\n\n`supplier_id`, `seq_number`, `status`, computed totals and the\ntimestamps are NOT patchable; line items are managed on their\nown sub-resource.\n",
        "properties": {
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "invoice_number": {
            "type": [
              "string",
              "null"
            ]
          },
          "expense_category_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "payment_method": {
            "type": [
              "string",
              "null"
            ]
          },
          "payment_due_at": {
            "$ref": "#/components/schemas/WritableTimestampNullable"
          },
          "total_before_tax": {
            "type": "number"
          },
          "total_tax": {
            "type": "number"
          },
          "comments": {
            "type": [
              "string",
              "null"
            ]
          }
        },
        "additionalProperties": false
      },
      "ExpensePayment": {
        "type": "object",
        "description": "ExpensePayment — one row of the expense payments ledger.\n",
        "required": [
          "object",
          "id",
          "amount",
          "payment_method",
          "payment_date",
          "financial_account_id",
          "created_at"
        ],
        "properties": {
          "object": {
            "type": "string",
            "description": "always \"expense_payment\""
          },
          "id": {
            "type": "string"
          },
          "amount": {
            "type": "number"
          },
          "payment_method": {
            "type": "string"
          },
          "payment_date": {
            "type": "string",
            "format": "date-time"
          },
          "financial_account_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "ExpensePaymentCreateRequest": {
        "type": "object",
        "description": "ExpensePaymentCreateRequest — POST /v1/expenses/{id}/payments.\n",
        "required": [
          "amount",
          "payment_method",
          "payment_date",
          "financial_account_id"
        ],
        "properties": {
          "amount": {
            "type": "number"
          },
          "payment_method": {
            "type": [
              "string",
              "null"
            ]
          },
          "payment_date": {
            "type": [
              "string",
              "null"
            ],
            "description": "YYYY-MM-DD between 1900-01-01 and 2100-12-31 (same window as\n`WritableTimestamp`); default today. Out of range → 422\n`parameter_invalid`.\n"
          },
          "financial_account_id": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "ExpenseRecurringRow": {
        "type": "object",
        "description": "ExpenseRecurringRow — sibling expense generated by the same\nschedule.\n",
        "required": [
          "object",
          "id",
          "seq_number",
          "created_at",
          "status",
          "total_after_tax"
        ],
        "properties": {
          "object": {
            "type": "string",
            "description": "always \"expense_recurring_row\""
          },
          "id": {
            "type": "string"
          },
          "seq_number": {
            "type": "integer"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "status": {
            "type": "string"
          },
          "total_after_tax": {
            "type": "number"
          }
        }
      },
      "ExpenseScheduleInfo": {
        "type": "object",
        "description": "ExpenseScheduleInfo — the recurring schedule this expense belongs\nto.\n",
        "required": [
          "object",
          "id",
          "frequency_months"
        ],
        "properties": {
          "object": {
            "type": "string",
            "description": "always \"expense_schedule\""
          },
          "id": {
            "type": "string"
          },
          "frequency_months": {
            "type": "integer"
          }
        }
      },
      "ExpenseStatusCounts": {
        "type": "object",
        "description": "ExpenseStatusCounts backs the list tabs (GET\n/v1/expenses/status-counts).\n",
        "required": [
          "object",
          "to_pay",
          "expired",
          "paid",
          "date_filter"
        ],
        "properties": {
          "object": {
            "type": "string",
            "description": "always \"expense_status_counts\""
          },
          "to_pay": {
            "type": "integer"
          },
          "expired": {
            "type": "integer"
          },
          "paid": {
            "type": "integer"
          },
          "date_filter": {
            "$ref": "#/components/schemas/DateFilter"
          }
        }
      },
      "FinancialAccount": {
        "type": "object",
        "description": "FinancialAccount: one of the company's conti (Banca, Cassa, …) —\nthe options of the prima nota Conto filter, mirroring the web\ncashbook's account dropdown. Read-only on this API.\n",
        "required": [
          "object",
          "id",
          "type",
          "name"
        ],
        "properties": {
          "object": {
            "type": "string",
            "description": "always \"financial_account\""
          },
          "id": {
            "type": "string"
          },
          "type": {
            "type": "string",
            "description": "e.g. \"bank\" | \"cash\""
          },
          "name": {
            "type": "string"
          }
        }
      },
      "LicensePlateLookupRequest": {
        "type": "object",
        "description": "LicensePlateLookupRequest: body of POST\n/v1/vehicles/license-plate-lookup.\n",
        "required": [
          "license_plate"
        ],
        "properties": {
          "license_plate": {
            "type": "string"
          }
        }
      },
      "LogoutRequest": {
        "type": "object",
        "description": "LogoutRequest: POST /v1/auth/logout body. The refresh token\nidentifies the session to revoke; sending it (rather than relying\non the bearer) lets sign-out work even after the short-lived\naccess token has expired.\n",
        "required": [
          "refresh_token"
        ],
        "properties": {
          "refresh_token": {
            "type": "string"
          }
        }
      },
      "ManufacturerListEnvelope": {
        "type": "object",
        "description": "ManufacturerListEnvelope: success body for GET /v1/manufacturers.\nFollows the standard list envelope; the collection is unpaginated\n(small, rarely-changing catalog) so HasMore is always false.\n",
        "required": [
          "object",
          "data",
          "has_more",
          "url"
        ],
        "properties": {
          "object": {
            "type": "string",
            "description": "always \"list\""
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Manufacturer"
            }
          },
          "has_more": {
            "type": "boolean"
          },
          "url": {
            "type": "string"
          }
        }
      },
      "Order": {
        "type": "object",
        "description": "Order is one \"Arrivi Merce\" row — a supplier order (ordine\nfornitore). List responses carry the header fields only; line\nitems live on the (not-yet-built) detail endpoint. status is one\nof the web lifecycle values: \"pending\" (In attesa) / \"delivery\"\n(In consegna) / \"received\" (Ricevuto).\n",
        "required": [
          "object",
          "id",
          "seq_number",
          "supplier_id",
          "supplier_name",
          "supplier_invoice_reference",
          "status",
          "total_before_tax",
          "total_tax",
          "total_after_tax",
          "total_after_discount",
          "payment_method",
          "comments",
          "invoice_date",
          "confirmed_at",
          "received_at",
          "created_at"
        ],
        "properties": {
          "object": {
            "type": "string",
            "description": "always \"order\""
          },
          "id": {
            "type": "string"
          },
          "seq_number": {
            "type": "integer"
          },
          "supplier_id": {
            "type": "string"
          },
          "supplier_name": {
            "type": "string"
          },
          "supplier_invoice_reference": {
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "type": "string"
          },
          "total_before_tax": {
            "type": "number"
          },
          "total_tax": {
            "type": "number"
          },
          "total_after_tax": {
            "type": "number"
          },
          "total_after_discount": {
            "type": "number"
          },
          "payment_method": {
            "type": "string"
          },
          "comments": {
            "type": [
              "string",
              "null"
            ]
          },
          "invoice_date": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "confirmed_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "received_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "OrderStatusCounts": {
        "type": "object",
        "description": "OrderStatusCounts backs the \"Arrivi Merce\" status tabs. Counts are\nscoped to the applied date range (echoed back in date_filter) and\nthe free-text search, mirroring the web list's per-status tab\ncounters.\n",
        "required": [
          "object",
          "pending",
          "delivery",
          "received",
          "date_filter"
        ],
        "properties": {
          "object": {
            "type": "string",
            "description": "always \"order_status_counts\""
          },
          "pending": {
            "type": "integer"
          },
          "delivery": {
            "type": "integer"
          },
          "received": {
            "type": "integer"
          },
          "date_filter": {
            "$ref": "#/components/schemas/DateFilter"
          }
        }
      },
      "PaymentListEnvelope": {
        "type": "object",
        "description": "PaymentListEnvelope: success body for GET\n/v1/worksheets/{id}/payments. Unpaginated — a worksheet carries a\nhandful of payments at most.\n",
        "required": [
          "object",
          "data",
          "has_more",
          "url"
        ],
        "properties": {
          "object": {
            "type": "string",
            "description": "always \"list\""
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Payment"
            }
          },
          "has_more": {
            "type": "boolean"
          },
          "url": {
            "type": "string"
          }
        }
      },
      "ProfileModules": {
        "type": "object",
        "description": "ProfileModules: the company feature flags the app UI gates on,\nresolved server-side from the company row with the same semantics\nthe web sidebar reads them. technical_data is the composite\nCompany.CanUseTechnicalData() (module flag AND active\nsubscription); has_worksheets backs the web's \"Situazione appears\nafter the first worksheet\" rule.\n",
        "required": [
          "checkins",
          "electronic_invoices",
          "expenses",
          "dealership",
          "vehicle_rental",
          "tyre_storage",
          "service_catalog",
          "inventory",
          "technical_data",
          "license_plate_search",
          "public_profile",
          "ai_assistant",
          "has_worksheets"
        ],
        "properties": {
          "checkins": {
            "type": "boolean"
          },
          "electronic_invoices": {
            "type": "boolean"
          },
          "expenses": {
            "type": "boolean"
          },
          "dealership": {
            "type": "boolean"
          },
          "vehicle_rental": {
            "type": "boolean"
          },
          "tyre_storage": {
            "type": "boolean"
          },
          "service_catalog": {
            "type": "boolean"
          },
          "inventory": {
            "type": "boolean"
          },
          "technical_data": {
            "type": "boolean"
          },
          "license_plate_search": {
            "type": "boolean"
          },
          "public_profile": {
            "type": "boolean"
          },
          "ai_assistant": {
            "type": "boolean"
          },
          "has_worksheets": {
            "type": "boolean"
          }
        }
      },
      "ProfileUpdateRequest": {
        "type": "object",
        "description": "ProfileUpdateRequest: body of PATCH /v1/me. first_name / last_name\nrequired (web parity) phone required — Italian mobile, national\nnumber or full +39 form (NormalizeItalianMobilePhone) password\noptional — set only to change it (min 6 chars, web parity); the\nrepeat-password check is client-side, like the web form\n",
        "required": [
          "first_name",
          "last_name",
          "phone",
          "password"
        ],
        "properties": {
          "first_name": {
            "type": "string"
          },
          "last_name": {
            "type": "string"
          },
          "phone": {
            "type": "string"
          },
          "password": {
            "type": "string"
          }
        }
      },
      "Receipt": {
        "type": "object",
        "description": "Receipt is the serialized form of entity.Receipt for the\n/v1/receipts endpoints. Same conventions as Invoice: money floats\nare display values, computed names are inlined, line_items appear\non detail responses only.\n",
        "required": [
          "object",
          "id",
          "title",
          "comments",
          "internal_comments",
          "email",
          "customer_id",
          "vehicle_id",
          "worksheet_id",
          "sezionale_id",
          "seq_number",
          "type",
          "status",
          "issued_at",
          "voided_at",
          "payment_method",
          "total_before_tax",
          "total_tax",
          "total_after_tax",
          "total_discount",
          "total_after_discount",
          "total_payments",
          "outstanding_payments",
          "customer_name",
          "vehicle_name",
          "vehicle_license",
          "created_by_full_name",
          "sezionale_code",
          "e_receipt_status",
          "e_receipt_document_number",
          "electronic_receipts_enabled",
          "created_at",
          "updated_at",
          "deleted_at"
        ],
        "properties": {
          "object": {
            "type": "string",
            "description": "always \"receipt\""
          },
          "id": {
            "type": "string"
          },
          "title": {
            "type": [
              "string",
              "null"
            ]
          },
          "comments": {
            "type": [
              "string",
              "null"
            ]
          },
          "internal_comments": {
            "type": [
              "string",
              "null"
            ],
            "description": "Optional — the electronic receipt PDF is delivered here."
          },
          "email": {
            "type": [
              "string",
              "null"
            ]
          },
          "customer_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "vehicle_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "worksheet_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "sezionale_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "seq_number": {
            "type": "integer",
            "description": "Progressive number. 0 until the receipt is issued."
          },
          "type": {
            "type": "string",
            "description": "Derived: \"draft\" | \"issued\" | \"voided\"."
          },
          "status": {
            "type": "string",
            "description": "\"sale\" | \"return\"."
          },
          "issued_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "voided_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "payment_method": {
            "type": [
              "string",
              "null"
            ]
          },
          "total_before_tax": {
            "type": "number"
          },
          "total_tax": {
            "type": "number"
          },
          "total_after_tax": {
            "type": "number"
          },
          "total_discount": {
            "type": "number"
          },
          "total_after_discount": {
            "type": "number"
          },
          "total_payments": {
            "type": "number"
          },
          "outstanding_payments": {
            "type": "number"
          },
          "customer_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "vehicle_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "vehicle_license": {
            "type": [
              "string",
              "null"
            ]
          },
          "created_by_full_name": {
            "type": "string"
          },
          "sezionale_code": {
            "type": [
              "string",
              "null"
            ],
            "description": "Latest electronic-receipt transmission (empty when none / module off)."
          },
          "e_receipt_status": {
            "type": [
              "string",
              "null"
            ]
          },
          "e_receipt_document_number": {
            "type": [
              "string",
              "null"
            ],
            "description": "ElectronicReceiptsEnabled — the company transmits scontrini to the AdE."
          },
          "electronic_receipts_enabled": {
            "type": "boolean"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          },
          "deleted_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "Detail responses only."
          },
          "line_items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ReceiptLineItem"
            }
          }
        }
      },
      "ReceiptCreateRequest": {
        "type": "object",
        "description": "ReceiptCreateRequest — a mobile scontrino is always created from a\nworksheet (web \"Crea scontrino\" parity).\n",
        "required": [
          "worksheet_id",
          "payment_method"
        ],
        "properties": {
          "worksheet_id": {
            "type": "string"
          },
          "payment_method": {
            "type": "string"
          }
        }
      },
      "ReceiptLineItem": {
        "type": "object",
        "required": [
          "object",
          "id",
          "article_id",
          "description",
          "quantity",
          "unit_type",
          "unit_price",
          "unit_discount_rate",
          "tax_rate",
          "vat_code",
          "total_price",
          "created_at"
        ],
        "properties": {
          "object": {
            "type": "string",
            "description": "always \"receipt_line_item\""
          },
          "id": {
            "type": "string"
          },
          "article_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "description": {
            "type": "string"
          },
          "quantity": {
            "type": "number"
          },
          "unit_type": {
            "type": "string"
          },
          "unit_price": {
            "type": "number"
          },
          "unit_discount_rate": {
            "type": "number"
          },
          "tax_rate": {
            "type": "number"
          },
          "vat_code": {
            "type": [
              "string",
              "null"
            ]
          },
          "total_price": {
            "type": "number"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "ReceiptPatchRequest": {
        "type": "object",
        "description": "Body of `PATCH /v1/receipts/{id}`. Three-state semantics\n(omitted = leave unchanged, explicit `null` = clear, concrete\nvalue = set).\n\n`seq_number`, `status`, `type`, the customer/vehicle/worksheet\nreferences, totals and the timestamps are NOT patchable; line\nitems are managed on their own sub-resource.\n",
        "properties": {
          "title": {
            "type": [
              "string",
              "null"
            ]
          },
          "comments": {
            "type": [
              "string",
              "null"
            ]
          },
          "internal_comments": {
            "type": [
              "string",
              "null"
            ]
          },
          "email": {
            "type": [
              "string",
              "null"
            ]
          },
          "payment_method": {
            "type": [
              "string",
              "null"
            ]
          },
          "sezionale_id": {
            "type": [
              "string",
              "null"
            ]
          }
        },
        "additionalProperties": false
      },
      "ReceiptStatusCounts": {
        "type": "object",
        "description": "ReceiptStatusCounts — the scontrini list document-status tab\ncounters (Bozze / Emessi / Annullati), computed over the active\nSDI + search filters, web parity.\n",
        "required": [
          "object",
          "draft",
          "issued",
          "voided"
        ],
        "properties": {
          "object": {
            "type": "string",
            "description": "always \"receipt_status_counts\""
          },
          "draft": {
            "type": "integer"
          },
          "issued": {
            "type": "integer"
          },
          "voided": {
            "type": "integer"
          }
        }
      },
      "RefreshRequest": {
        "type": "object",
        "description": "RefreshRequest: POST /v1/auth/refresh body. The opaque refresh\ntoken IS the credential (this endpoint takes no bearer), exchanged\nfor a rotated token pair.\n",
        "required": [
          "refresh_token"
        ],
        "properties": {
          "refresh_token": {
            "type": "string"
          }
        }
      },
      "ReportAppointment": {
        "type": "object",
        "description": "ReportAppointment is one row of widget \"upcoming-appointments\".\n",
        "required": [
          "id",
          "title",
          "customer_name",
          "start_time"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "customer_name": {
            "type": "string"
          },
          "start_time": {
            "type": "string",
            "description": "RFC 3339"
          }
        }
      },
      "ReportChartSeries": {
        "type": "object",
        "description": "ReportChartSeries is one named series of a stacked chart, aligned\nwith the envelope's XAxis.\n",
        "required": [
          "key",
          "values"
        ],
        "properties": {
          "key": {
            "type": "string"
          },
          "values": {
            "type": "array",
            "items": {
              "type": "number"
            }
          }
        }
      },
      "ReportKPI": {
        "type": "object",
        "description": "ReportKPI is one headline stat tile (widget \"kpis\" returns four).\n",
        "required": [
          "key",
          "value",
          "is_currency",
          "previous_value",
          "delta",
          "delta_percentage"
        ],
        "properties": {
          "key": {
            "type": "string",
            "description": "sales | outstanding | worksheets | customers"
          },
          "value": {
            "type": "number"
          },
          "is_currency": {
            "type": "boolean"
          },
          "previous_value": {
            "type": "number"
          },
          "delta": {
            "type": "number"
          },
          "delta_percentage": {
            "type": "number"
          }
        }
      },
      "ReportOutstandingWorksheet": {
        "type": "object",
        "description": "ReportOutstandingWorksheet is one row of widget\n\"outstanding-worksheets\". BarWidth is the server-normalized bar\nlength (0-100).\n",
        "required": [
          "worksheet_id",
          "title",
          "customer_name",
          "outstanding",
          "total",
          "bar_width"
        ],
        "properties": {
          "worksheet_id": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "customer_name": {
            "type": "string"
          },
          "outstanding": {
            "type": "number"
          },
          "total": {
            "type": "number"
          },
          "bar_width": {
            "type": "number"
          }
        }
      },
      "ReportRankedRow": {
        "type": "object",
        "description": "ReportRankedRow is one bar of the ranked-list widgets\n(\"top-customers\", \"top-services\", \"worksheet-margins\"). Amount\ncarries the sort metric; BarWidth is server-normalized (0-100).\n",
        "required": [
          "id",
          "label",
          "amount",
          "bar_width"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "label": {
            "type": "string"
          },
          "sublabel": {
            "type": "string"
          },
          "amount": {
            "type": "number"
          },
          "percent": {
            "type": "number"
          },
          "bar_width": {
            "type": "number"
          }
        }
      },
      "RoadmapBoard": {
        "type": "object",
        "description": "Roadmap (Novità / Funzionalità in sviluppo) — web\nsettings-roadmap.\n",
        "required": [
          "object",
          "planned",
          "progress",
          "done"
        ],
        "properties": {
          "object": {
            "type": "string",
            "description": "always \"roadmap_board\""
          },
          "planned": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/RoadmapItem"
            }
          },
          "progress": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/RoadmapItem"
            }
          },
          "done": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/RoadmapItem"
            }
          }
        }
      },
      "RoadmapItem": {
        "type": "object",
        "required": [
          "object",
          "id",
          "title",
          "description",
          "votes",
          "has_voted"
        ],
        "properties": {
          "object": {
            "type": "string",
            "description": "always \"roadmap_item\""
          },
          "id": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "description": {
            "type": "string",
            "description": "Display count (baseline 1 + user votes, web DisplayVotes)."
          },
          "votes": {
            "type": "integer"
          },
          "has_voted": {
            "type": "boolean"
          }
        }
      },
      "RoadmapSuggestionRequest": {
        "type": "object",
        "required": [
          "name",
          "description"
        ],
        "properties": {
          "name": {
            "type": "string"
          },
          "description": {
            "type": "string"
          }
        }
      },
      "SendChannelEmail": {
        "type": "object",
        "description": "SendChannelEmail: defaults for the email form. Free (no credits).\n",
        "required": [
          "recipient",
          "subject",
          "body",
          "senders"
        ],
        "properties": {
          "recipient": {
            "type": "string",
            "description": "customer email (\"\" when unset)"
          },
          "subject": {
            "type": "string",
            "description": "max 50 chars on send"
          },
          "body": {
            "type": "string",
            "description": "editable, share link included"
          },
          "senders": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/EmailSender"
            },
            "description": "exactly one has default=true"
          }
        }
      },
      "SendChannelSMS": {
        "type": "object",
        "description": "SendChannelSMS: defaults for the SMS form. Body must stay within\nmax_length and the GSM-7 basic charset (no accents/emoji).\nRecipient is the customer's stored E.164 phone: messaging is\nItalian-only, so the client shows an editable +39 national-number\nfield when it's empty or +39, and a read-only field with a\ndisabled send otherwise (web message-phone-field parity).\n",
        "required": [
          "recipient",
          "body",
          "max_length",
          "credit_cost"
        ],
        "properties": {
          "recipient": {
            "type": "string",
            "description": "customer phone (\"\" when unset)"
          },
          "body": {
            "type": "string"
          },
          "max_length": {
            "type": "integer",
            "const": 160,
            "description": "The SMS body limit, in GSM-7 characters."
          },
          "credit_cost": {
            "type": "number",
            "description": "0 = included in the plan"
          }
        }
      },
      "SendChannelWhatsApp": {
        "type": "object",
        "description": "SendChannelWhatsApp: the WhatsApp Business flow sends a\npre-approved template — template_preview is display-only, never\neditable or submitted. Same Italian-only recipient rules as SMS.\n",
        "required": [
          "recipient",
          "template_preview",
          "credit_cost"
        ],
        "properties": {
          "recipient": {
            "type": "string"
          },
          "template_preview": {
            "type": "string"
          },
          "credit_cost": {
            "type": "number",
            "description": "0 = included in the plan"
          }
        }
      },
      "Session": {
        "type": "object",
        "description": "Session: one entry in the \"Sessioni attive\" list (GET\n/v1/sessions) and the object returned by DELETE /v1/sessions/{id}.\nNever exposes the token hash or raw IP — only what the user needs\nto recognise and manage a device.\n",
        "required": [
          "object",
          "id",
          "surface",
          "display_name",
          "created_at",
          "last_used_at",
          "active",
          "current"
        ],
        "properties": {
          "object": {
            "type": "string",
            "description": "always \"session\""
          },
          "id": {
            "type": "string"
          },
          "surface": {
            "type": "string",
            "description": "web | mobile"
          },
          "display_name": {
            "type": "string"
          },
          "email": {
            "type": "string",
            "description": "team member the device belongs to (owner-only company listing)"
          },
          "platform": {
            "type": "string"
          },
          "app_version": {
            "type": "string"
          },
          "os_version": {
            "type": "string"
          },
          "location": {
            "type": "string",
            "description": "\"Milano, IT\" — from Cloudflare geo"
          },
          "last_ip": {
            "type": "string"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "last_used_at": {
            "type": "string",
            "format": "date-time"
          },
          "active": {
            "type": "boolean",
            "description": "true when the token is still live (not revoked/expired)"
          },
          "current": {
            "type": "boolean",
            "description": "true on the caller's own session"
          }
        }
      },
      "SessionDeletedResponse": {
        "type": "object",
        "description": "SessionDeletedResponse: body for DELETE /v1/sessions/{id} and POST\n/v1/auth/logout.\n",
        "required": [
          "object",
          "id",
          "deleted"
        ],
        "properties": {
          "object": {
            "type": "string",
            "description": "always \"session\""
          },
          "id": {
            "type": "string"
          },
          "deleted": {
            "type": "boolean"
          }
        }
      },
      "SessionRevocationResponse": {
        "type": "object",
        "description": "SessionRevocationResponse: body for POST\n/v1/sessions/revoke-others.\n",
        "required": [
          "object",
          "revoked_count"
        ],
        "properties": {
          "object": {
            "type": "string",
            "description": "always \"session_revocation\""
          },
          "revoked_count": {
            "type": "integer"
          }
        }
      },
      "Supplier": {
        "type": "object",
        "description": "Supplier (fornitore).\n",
        "required": [
          "object",
          "id",
          "name",
          "email",
          "phone",
          "phone_national",
          "address",
          "city",
          "zip_code",
          "tax_id",
          "comments",
          "bank_account_iban",
          "website",
          "website_display",
          "supplier_type",
          "created_at",
          "updated_at",
          "deleted_at"
        ],
        "properties": {
          "object": {
            "type": "string",
            "description": "always \"supplier\""
          },
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "email": {
            "type": [
              "string",
              "null"
            ]
          },
          "phone": {
            "type": [
              "string",
              "null"
            ]
          },
          "phone_national": {
            "type": [
              "string",
              "null"
            ],
            "description": "The phone number without the +39 country code."
          },
          "address": {
            "type": [
              "string",
              "null"
            ]
          },
          "city": {
            "type": [
              "string",
              "null"
            ]
          },
          "zip_code": {
            "type": [
              "string",
              "null"
            ]
          },
          "tax_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "comments": {
            "type": [
              "string",
              "null"
            ]
          },
          "bank_account_iban": {
            "type": [
              "string",
              "null"
            ]
          },
          "website": {
            "type": [
              "string",
              "null"
            ]
          },
          "website_display": {
            "type": [
              "string",
              "null"
            ],
            "description": "The website without the scheme, as shown in the app."
          },
          "supplier_type": {
            "type": "string"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          },
          "deleted_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "Detail-only compact rows for the web supplier-detail tabs"
          },
          "orders": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SupplierOrderRow"
            }
          },
          "articles": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SupplierArticleRow"
            }
          },
          "expenses": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SupplierExpenseRow"
            }
          }
        }
      },
      "SupplierArticleRow": {
        "type": "object",
        "required": [
          "object",
          "id",
          "code",
          "seq_number",
          "name",
          "stock",
          "sale_price",
          "created_at"
        ],
        "properties": {
          "object": {
            "type": "string",
            "description": "always \"supplier_article\""
          },
          "id": {
            "type": "string"
          },
          "code": {
            "type": [
              "string",
              "null"
            ]
          },
          "seq_number": {
            "type": "integer"
          },
          "name": {
            "type": "string"
          },
          "stock": {
            "type": "number"
          },
          "sale_price": {
            "type": [
              "number",
              "null"
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "SupplierCreateRequest": {
        "type": "object",
        "required": [
          "name",
          "email",
          "phone",
          "address",
          "city",
          "zip_code",
          "tax_id",
          "comments",
          "bank_account_iban",
          "website"
        ],
        "properties": {
          "name": {
            "type": "string"
          },
          "email": {
            "type": [
              "string",
              "null"
            ]
          },
          "phone": {
            "type": [
              "string",
              "null"
            ]
          },
          "address": {
            "type": [
              "string",
              "null"
            ]
          },
          "city": {
            "type": [
              "string",
              "null"
            ]
          },
          "zip_code": {
            "type": [
              "string",
              "null"
            ]
          },
          "tax_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "comments": {
            "type": [
              "string",
              "null"
            ]
          },
          "bank_account_iban": {
            "type": [
              "string",
              "null"
            ]
          },
          "website": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "SupplierExpenseRow": {
        "type": "object",
        "required": [
          "object",
          "id",
          "seq_number",
          "created_at",
          "invoice_number",
          "payment_due_at",
          "total_after_tax",
          "status"
        ],
        "properties": {
          "object": {
            "type": "string",
            "description": "always \"supplier_expense\""
          },
          "id": {
            "type": "string"
          },
          "seq_number": {
            "type": "integer"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "invoice_number": {
            "type": [
              "string",
              "null"
            ]
          },
          "payment_due_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "total_after_tax": {
            "type": "number"
          },
          "status": {
            "type": "string",
            "description": "to_pay | paid"
          }
        }
      },
      "SupplierOrderRow": {
        "type": "object",
        "required": [
          "object",
          "id",
          "seq_number",
          "created_at",
          "status",
          "total_after_tax"
        ],
        "properties": {
          "object": {
            "type": "string",
            "description": "always \"supplier_order\""
          },
          "id": {
            "type": "string"
          },
          "seq_number": {
            "type": "integer"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "status": {
            "type": "string",
            "description": "pending | delivery | received"
          },
          "total_after_tax": {
            "type": "number"
          }
        }
      },
      "SupplierPatchRequest": {
        "type": "object",
        "description": "Body of `PATCH /v1/suppliers/{id}`. Three-state semantics\n(omitted = leave unchanged, explicit `null` = clear, concrete\nvalue = set) on every field but `name`,\nwhich is not nullable and ignores an explicit `null`.\n\n`supplier_type`, `phone_national`, `website_display` and the\ntimestamps are NOT patchable: the two display fields are derived\nfrom `phone` and `website`.\n",
        "properties": {
          "name": {
            "type": "string"
          },
          "email": {
            "type": [
              "string",
              "null"
            ]
          },
          "phone": {
            "type": [
              "string",
              "null"
            ],
            "description": "Stored as +39…; a national number is normalised."
          },
          "address": {
            "type": [
              "string",
              "null"
            ]
          },
          "city": {
            "type": [
              "string",
              "null"
            ]
          },
          "zip_code": {
            "type": [
              "string",
              "null"
            ]
          },
          "tax_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "comments": {
            "type": [
              "string",
              "null"
            ]
          },
          "bank_account_iban": {
            "type": [
              "string",
              "null"
            ]
          },
          "website": {
            "type": [
              "string",
              "null"
            ],
            "description": "Stored with the scheme; `https://` is added when missing."
          }
        },
        "additionalProperties": false
      },
      "TyreSet": {
        "type": "object",
        "description": "TyreSet (treno gomme) — deposito gomme.\n",
        "required": [
          "object",
          "id",
          "seq_number",
          "description",
          "status",
          "quantity",
          "rim_type",
          "season_type",
          "condition",
          "residual_mm",
          "location",
          "comments",
          "customer_id",
          "vehicle_id",
          "customer_name",
          "vehicle_name",
          "vehicle_license",
          "deposited_at",
          "mounted_at",
          "collected_at",
          "disposed_at",
          "inventory_check_at",
          "created_at",
          "updated_at",
          "deleted_at"
        ],
        "properties": {
          "object": {
            "type": "string",
            "description": "always \"tyre_set\""
          },
          "id": {
            "type": "string"
          },
          "seq_number": {
            "type": "integer"
          },
          "description": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "description": "deposited | mounted | disposed | collected"
          },
          "quantity": {
            "type": "integer"
          },
          "rim_type": {
            "type": "string",
            "description": "steel | alloy"
          },
          "season_type": {
            "type": [
              "string",
              "null"
            ]
          },
          "condition": {
            "type": [
              "string",
              "null"
            ]
          },
          "residual_mm": {
            "type": [
              "integer",
              "null"
            ]
          },
          "location": {
            "type": [
              "string",
              "null"
            ]
          },
          "comments": {
            "type": [
              "string",
              "null"
            ]
          },
          "customer_id": {
            "type": "string"
          },
          "vehicle_id": {
            "type": "string"
          },
          "customer_name": {
            "type": "string"
          },
          "vehicle_name": {
            "type": "string"
          },
          "vehicle_license": {
            "type": [
              "string",
              "null"
            ]
          },
          "deposited_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "mounted_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "collected_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "disposed_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "inventory_check_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          },
          "deleted_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "TyreSetCreateRequest": {
        "type": "object",
        "description": "TyreSetCreateRequest doubles as the full-form PATCH body (customer\nand vehicle are ignored on update).\n",
        "required": [
          "customer_id",
          "vehicle_id",
          "description",
          "quantity",
          "rim_type",
          "season_type",
          "condition",
          "residual_mm",
          "location",
          "comments"
        ],
        "properties": {
          "customer_id": {
            "type": "string"
          },
          "vehicle_id": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "quantity": {
            "type": [
              "integer",
              "null"
            ]
          },
          "rim_type": {
            "type": "string"
          },
          "season_type": {
            "type": [
              "string",
              "null"
            ]
          },
          "condition": {
            "type": [
              "string",
              "null"
            ]
          },
          "residual_mm": {
            "type": [
              "integer",
              "null"
            ]
          },
          "location": {
            "type": [
              "string",
              "null"
            ]
          },
          "comments": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "TyreSetStatusCounts": {
        "type": "object",
        "required": [
          "object",
          "deposited",
          "mounted",
          "disposed",
          "collected",
          "locations"
        ],
        "properties": {
          "object": {
            "type": "string",
            "description": "always \"tyre_set_status_counts\""
          },
          "deposited": {
            "type": "integer"
          },
          "mounted": {
            "type": "integer"
          },
          "disposed": {
            "type": "integer"
          },
          "collected": {
            "type": "integer",
            "description": "Distinct locations in use — drives the Ubicazione filter, which the"
          },
          "locations": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "VatRateCatalogEntry": {
        "type": "object",
        "description": "VatRateCatalogEntry — one global VAT rate with the company's\nenabled mark (GET /v1/vat-rates/catalog).\n",
        "required": [
          "object",
          "code",
          "rate",
          "description",
          "enabled"
        ],
        "properties": {
          "object": {
            "type": "string",
            "description": "always \"vat_rate_catalog_entry\""
          },
          "code": {
            "type": "string"
          },
          "rate": {
            "type": "number"
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "enabled": {
            "type": "boolean"
          }
        }
      },
      "VehicleCustomField": {
        "type": "object",
        "description": "VehicleCustomField: one company-configured custom field resolved\nfor display and editing — id from the definition, its display\nname, its input type (string | integer | datetime) and this\nvehicle's value (\"\" when unset).\n",
        "required": [
          "id",
          "name",
          "type",
          "value"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "type": {
            "type": "string"
          },
          "value": {
            "type": "string"
          }
        }
      },
      "VehicleFieldVisibility": {
        "type": "object",
        "description": "VehicleFieldVisibility mirrors the company's optional-field\ntoggles (entity.VehicleFieldVisibility) so the edit form hides\nexactly what the web form hides.\n",
        "required": [
          "km",
          "numero_telaio",
          "transmission",
          "engine_category",
          "engine_power_kw",
          "engine_displacement_ccm",
          "vehicle_weight_kg",
          "registration_date",
          "last_inspection_date",
          "next_inspection_date"
        ],
        "properties": {
          "km": {
            "type": "boolean"
          },
          "numero_telaio": {
            "type": "boolean"
          },
          "transmission": {
            "type": "boolean"
          },
          "engine_category": {
            "type": "boolean"
          },
          "engine_power_kw": {
            "type": "boolean"
          },
          "engine_displacement_ccm": {
            "type": "boolean"
          },
          "vehicle_weight_kg": {
            "type": "boolean"
          },
          "registration_date": {
            "type": "boolean"
          },
          "last_inspection_date": {
            "type": "boolean"
          },
          "next_inspection_date": {
            "type": "boolean"
          }
        }
      }
    },
    "responses": {
      "Unauthorized": {
        "x-description-it": "Chiave API mancante, non valida, revocata o scaduta.",
        "description": "Missing, malformed, expired, or wrong-audience bearer token.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorEnvelope"
            },
            "example": {
              "error": {
                "type": "https://api.officina.it/errors/api_key_invalid",
                "status": 401,
                "code": "api_key_invalid",
                "message": "Chiave API mancante, non valida, revocata o scaduta.",
                "trace_id": "abc-123"
              }
            }
          }
        }
      },
      "PaymentRequired": {
        "x-description-it": "Il piano non include l'accesso API, oppure non è attivo un abbonamento.",
        "description": "The company has no active subscription, or its plan does not\ninclude API access. API access is included in the Business and\nIllimitato plans.\n",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorEnvelope"
            },
            "example": {
              "error": {
                "type": "https://api.officina.it/errors/plan_upgrade_required",
                "status": 402,
                "code": "plan_upgrade_required",
                "message": "Il piano attivo non include l'accesso API.",
                "trace_id": "abc-123"
              }
            }
          }
        }
      },
      "Forbidden": {
        "x-description-it": "La chiave non ha il permesso richiesto per questa operazione.",
        "description": "The authenticated user lacks the required permission.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorEnvelope"
            },
            "example": {
              "error": {
                "type": "https://api.officina.it/errors/insufficient_scope",
                "status": 403,
                "code": "insufficient_scope",
                "message": "La chiave non ha il permesso reports.read.",
                "trace_id": "abc-123"
              }
            }
          }
        }
      },
      "NotFound": {
        "x-description-it": "La risorsa non esiste, è stata eliminata o non appartiene alla tua officina.",
        "description": "Resource does not exist OR belongs to a different company.\nThe two cases are intentionally indistinguishable.\n",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorEnvelope"
            },
            "example": {
              "error": {
                "type": "https://api.officina.it/errors/not_found",
                "status": 404,
                "code": "not_found",
                "message": "Customer not found.",
                "trace_id": "abc-123"
              }
            }
          }
        }
      },
      "InvalidCredentials": {
        "x-description-it": "Credenziali non corrette.",
        "description": "Email or password incorrect.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorEnvelope"
            },
            "example": {
              "error": {
                "type": "https://api.officina.it/errors/invalid_credentials",
                "status": 401,
                "code": "invalid_credentials",
                "message": "The email or password you provided is incorrect.",
                "trace_id": "abc-123"
              }
            }
          }
        }
      },
      "SignOnTokenInvalid": {
        "x-description-it": "Codice di accesso non valido o scaduto.",
        "description": "Sign-on token invalid or expired.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorEnvelope"
            },
            "example": {
              "error": {
                "type": "https://api.officina.it/errors/sign_on_token_invalid",
                "status": 401,
                "code": "sign_on_token_invalid",
                "message": "The sign-on token is invalid or has expired.",
                "param": "/sign_on_token",
                "trace_id": "abc-123"
              }
            }
          }
        }
      },
      "ValidationError": {
        "x-description-it": "Un campo della richiesta manca o non è valido: «param» indica quale.",
        "description": "A request field is missing or invalid (fast-fail).",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorEnvelope"
            },
            "example": {
              "error": {
                "type": "https://api.officina.it/errors/parameter_invalid",
                "status": 422,
                "code": "parameter_invalid",
                "message": "Email must be a valid email address.",
                "param": "/email",
                "trace_id": "abc-123"
              }
            }
          }
        }
      },
      "MalformedRequest": {
        "x-description-it": "Corpo della richiesta non leggibile o JSON non valido.",
        "description": "Request body is unparseable or X-API-Version is missing.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorEnvelope"
            },
            "example": {
              "error": {
                "type": "https://api.officina.it/errors/malformed_request",
                "status": 400,
                "code": "malformed_request",
                "message": "Request body is not valid JSON.",
                "trace_id": "abc-123"
              }
            }
          }
        }
      },
      "UnsupportedMediaType": {
        "x-description-it": "Il Content-Type deve essere application/json e Accept deve accettare JSON.",
        "description": "Content-Type is not `application/json`, or Accept rejects JSON.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorEnvelope"
            },
            "example": {
              "error": {
                "type": "https://api.officina.it/errors/unsupported_media_type",
                "status": 415,
                "code": "unsupported_media_type",
                "message": "Content-Type must be application/json.",
                "trace_id": "abc-123"
              }
            }
          }
        }
      },
      "Conflict": {
        "x-description-it": "L'operazione è in conflitto con lo stato attuale della risorsa.",
        "description": "The request conflicts with the current state of the\nresource. Examples: issuing an invoice that has already\nbeen issued; back-dating an issuance that would violate the\nmonotonic per-company sequence; deleting a credit note\nwhose SDI electronic counterpart has already been\ntransmitted and cannot be retracted; mutating a worksheet\nwhose linked invoice has been issued (the worksheet is\nfrozen — its totals are now part of the issued invoice's\naccounting record and must not desync).\n",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorEnvelope"
            },
            "example": {
              "error": {
                "type": "https://api.officina.it/errors/conflict",
                "status": 409,
                "code": "conflict",
                "message": "Worksheet is frozen: its linked invoice has been issued. Cancel the invoice with a credit note before editing.",
                "trace_id": "abc-123"
              }
            }
          }
        }
      },
      "InternalError": {
        "x-description-it": "Errore imprevisto del server. Il dettaglio finisce nei log, non nella risposta.",
        "description": "Unhandled server error. Detail is logged, not exposed.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorEnvelope"
            },
            "example": {
              "error": {
                "type": "https://api.officina.it/errors/internal_error",
                "status": 500,
                "code": "internal_error",
                "message": "An unexpected error occurred. Please retry or contact support if it persists.",
                "trace_id": "abc-123"
              }
            }
          }
        }
      }
    }
  }
}
