Preventivi

Preventivi, righe e conversione in scheda lavoro.

12 endpoint Versione v1 · revisione 2026-10-31

Gli endpoint sono relativi a https://api.officina.it e richiedono l'header Authorization: Bearer con la chiave API e X-API-Version: 2026-10-31. L'header della revisione è obbligatorio: differenza fra v1 e revisione; le novità di ogni revisione sono nell'elenco delle revisioni.

GET /v1/quotes
Permesso richiesto quotes.read

Elenca i preventivi

I preventivi dell'officina, dal più recente. Filtra per stato con status, per cliente con customer_id, per veicolo con vehicle_id e per periodo con from / to. La paginazione usa un cursore opaco: passa next_starting_after invariato in starting_after e fermati quando has_more è false.
dalla revisione 2026-10-28

Nuovo stato draft (bozza): ogni preventivo nuovo nasce in bozza e passa a waiting quando lo si segna come inviato o quando parte un messaggio al cliente. Il filtro status accetta draft. Le revisioni precedenti vedono le bozze come waiting, anche nel filtro.

dalla revisione 2026-10-27

Nuovo campo expires_at: la data scadenza del preventivo (YYYY-MM-DD, null = nessuna scadenza).

dalla revisione 2026-10-03

Nuovi campi work_category_ids e work_categories: tutte le categorie del documento, nell'ordine scelto (work_category_id riporta il primo, per i client che ne gestiscono uno solo).

Risposte
200
Una pagina di preventivi.
401
Chiave API mancante, non valida, revocata o scaduta.
402
Il piano non include l'accesso API, oppure non è attivo un abbonamento.
403
La chiave non ha il permesso richiesto per questa operazione.
415
Il Content-Type deve essere application/json e Accept deve accettare JSON.
422
Un campo della richiesta manca o non è valido: «param» indica quale.
500
Errore imprevisto del server. Il dettaglio finisce nei log, non nella risposta.
Richiesta
curl https://api.officina.it/v1/quotes \
  -H "Authorization: Bearer ofk_live_LA_TUA_CHIAVE" \
  -H "X-API-Version: 2026-10-31"
Risposta
{
  "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,
      "work_category_ids": [],
      "work_categories": [],
      "km": 61240,
      "expires_at": "2026-08-15",
      "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"
  }
}
POST /v1/quotes
Permesso richiesto quotes.write

Crea un preventivo

Crea un preventivo in stato draft (bozza; waiting con revisioni precedenti alla 2026-10-28). Il numero progressivo (seq_number) è assegnato dal server. Le righe non si passano qui: crea il preventivo, poi aggiungile con POST /v1/quotes/{id}/line-items/batch.
dalla revisione 2026-10-28

Il preventivo nasce in stato draft (bozza). Con una revisione precedente nasce waiting, come prima.

dalla revisione 2026-10-27

Accetta expires_at (data scadenza, YYYY-MM-DD), che non può precedere la data del preventivo: altrimenti 422 su /expires_at. La risposta include expires_at.

dalla revisione 2026-10-03

Accetta work_category_ids per impostare più categorie (massimo 10); la risposta include work_category_ids e work_categories. work_category_id resta valido con il significato di sempre.

Risposte
201
Il preventivo creato.
400
Corpo della richiesta non leggibile o JSON non valido.
401
Chiave API mancante, non valida, revocata o scaduta.
402
Il piano non include l'accesso API, oppure non è attivo un abbonamento.
403
La chiave non ha il permesso richiesto per questa operazione.
415
Il Content-Type deve essere application/json e Accept deve accettare JSON.
422
Un campo della richiesta manca o non è valido: «param» indica quale.
500
Errore imprevisto del server. Il dettaglio finisce nei log, non nella risposta.
Richiesta
curl -X POST https://api.officina.it/v1/quotes \
  -H "Authorization: Bearer ofk_live_LA_TUA_CHIAVE" \
  -H "X-API-Version: 2026-10-31" \
  -H "Content-Type: application/json" \
  -d '{
  "customer_id": "4c911f08-7713-4e83-bdf6-a0692a1b43fd",
  "vehicle_id": "8821f0ac-5d2b-41e7-9a10-6c3f2b7d4e55",
  "title": "Preventivo tagliando",
  "km": 61240,
  "expires_at": "2026-08-15"
}'
Risposta
{
  "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,
  "work_category_ids": [],
  "work_categories": [],
  "km": 61240,
  "expires_at": "2026-08-15",
  "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"
}
GET /v1/quotes/by-number/{year}/{number}
Permesso richiesto quotes.read

Recupera il preventivo dal numero

Restituisce il preventivo con il numero number dell'anno year (anno della data del preventivo): lo stesso numero mostrato nell'app, per esempio 12/2026. Risponde come il recupero per id. Se il numero corrisponde a più documenti — numeri ripetuti nello storico — restituisce 409 ambiguous_number con gli id in candidates.
dalla revisione 2026-10-04

Nuovo: recupera il preventivo dal numero e dall'anno.

Risposte
200
Il preventivo, con la stessa risposta del recupero per id.
401
Chiave API mancante, non valida, revocata o scaduta.
402
Il piano non include l'accesso API, oppure non è attivo un abbonamento.
403
La chiave non ha il permesso richiesto per questa operazione.
404
La risorsa non esiste, è stata eliminata o non appartiene alla tua officina.
409
Il numero corrisponde a più documenti: scegli quello giusto tra i `candidates` e recuperalo per id.
422
Un campo della richiesta manca o non è valido: «param» indica quale.
500
Errore imprevisto del server. Il dettaglio finisce nei log, non nella risposta.
Richiesta
curl https://api.officina.it/v1/quotes/by-number/{year}/{number} \
  -H "Authorization: Bearer ofk_live_LA_TUA_CHIAVE" \
  -H "X-API-Version: 2026-10-31"
Risposta
{
  "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,
  "work_category_ids": [],
  "work_categories": [],
  "km": 61240,
  "expires_at": "2026-08-15",
  "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"
    }
  ]
}
GET /v1/quotes/status-counts
Permesso richiesto quotes.read

Conta i preventivi per stato

Il numero di preventivi in ciascuno stato, con gli stessi filtri dell'elenco. Serve a popolare i contatori senza scaricare i preventivi.
dalla revisione 2026-10-28

Nuovo contatore draft. Per le revisioni precedenti manca, e le bozze sono contate in waiting.

Risposte
200
Il numero di preventivi per stato.
401
Chiave API mancante, non valida, revocata o scaduta.
402
Il piano non include l'accesso API, oppure non è attivo un abbonamento.
403
La chiave non ha il permesso richiesto per questa operazione.
422
Un campo della richiesta manca o non è valido: «param» indica quale.
500
Errore imprevisto del server. Il dettaglio finisce nei log, non nella risposta.
Richiesta
curl https://api.officina.it/v1/quotes/status-counts \
  -H "Authorization: Bearer ofk_live_LA_TUA_CHIAVE" \
  -H "X-API-Version: 2026-10-31"
Risposta
{
  "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"
  }
}
GET /v1/quotes/{id}
Permesso richiesto quotes.read

Recupera un preventivo

Restituisce il preventivo con le righe (line_items) e i totali calcolati. Se il preventivo è già stato convertito, worksheet_id punta alla scheda lavoro generata.
dalla revisione 2026-10-28

status può valere draft (bozza). Le revisioni precedenti ricevono waiting.

dalla revisione 2026-10-27

Nuovo campo expires_at: la data scadenza del preventivo (YYYY-MM-DD, null = nessuna scadenza).

dalla revisione 2026-10-03

Nuovi campi work_category_ids e work_categories: tutte le categorie del documento, nell'ordine scelto (work_category_id riporta il primo, per i client che ne gestiscono uno solo).

Risposte
200
Il preventivo richiesto.
401
Chiave API mancante, non valida, revocata o scaduta.
402
Il piano non include l'accesso API, oppure non è attivo un abbonamento.
403
La chiave non ha il permesso richiesto per questa operazione.
404
La risorsa non esiste, è stata eliminata o non appartiene alla tua officina.
415
Il Content-Type deve essere application/json e Accept deve accettare JSON.
500
Errore imprevisto del server. Il dettaglio finisce nei log, non nella risposta.
Richiesta
curl https://api.officina.it/v1/quotes/{id} \
  -H "Authorization: Bearer ofk_live_LA_TUA_CHIAVE" \
  -H "X-API-Version: 2026-10-31"
Risposta
{
  "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,
  "work_category_ids": [],
  "work_categories": [],
  "km": 61240,
  "expires_at": "2026-08-15",
  "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"
    }
  ]
}
PATCH /v1/quotes/{id}
Permesso richiesto quotes.write

Aggiorna un preventivo

Aggiorna solo i campi presenti nel corpo: gli altri restano invariati, e null svuota il campo. Per cambiare stato usa POST /v1/quotes/{id}/transitions, non questo endpoint.
dalla revisione 2026-10-27

Accetta expires_at (data scadenza, YYYY-MM-DD; null la rimuove), che non può precedere la data del preventivo: altrimenti 422 su /expires_at.

dalla revisione 2026-10-22

If-Match obbligatorio da questa revisione: senza l'header la richiesta è rifiutata con 428 precondition_required (If-Match: * scrive comunque). Con una version superata risponde 409.

dalla revisione 2026-10-03

Accetta work_category_ids: sostituisce l'intero elenco delle categorie ([] o null li rimuove tutti). work_category_id continua a sostituire solo il primo dell'elenco. La risposta include work_category_ids e work_categories.

Risposte
200
Il preventivo aggiornato.
400
Corpo della richiesta non leggibile o JSON non valido.
401
Chiave API mancante, non valida, revocata o scaduta.
402
Il piano non include l'accesso API, oppure non è attivo un abbonamento.
403
La chiave non ha il permesso richiesto per questa operazione.
404
La risorsa non esiste, è stata eliminata o non appartiene alla tua officina.
409
Il documento è stato salvato nel frattempo (la `version` di `If-Match` è superata): ricaricalo e riprova. Nulla è stato salvato.
415
Il Content-Type deve essere application/json e Accept deve accettare JSON.
422
Un campo della richiesta manca o non è valido: «param» indica quale.
428
Manca l'header `If-Match` (obbligatorio dalla revisione 2026-10-22).
500
Errore imprevisto del server. Il dettaglio finisce nei log, non nella risposta.
Richiesta
curl -X PATCH https://api.officina.it/v1/quotes/{id} \
  -H "Authorization: Bearer ofk_live_LA_TUA_CHIAVE" \
  -H "X-API-Version: 2026-10-31" \
  -H "Content-Type: application/json" \
  -d '{
  "title": "Preventivo aggiornato",
  "comments": null,
  "expires_at": "2026-08-31"
}'
Risposta
{
  "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,
  "work_category_ids": [],
  "work_categories": [],
  "km": 61240,
  "expires_at": "2026-08-15",
  "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"
    }
  ]
}
DELETE /v1/quotes/{id}
Permesso richiesto quotes.write

Elimina un preventivo

Eliminazione logica: il preventivo sparisce dagli elenchi. La risposta contiene il preventivo con deleted_at valorizzato.
Risposte
200
Il preventivo eliminato.
401
Chiave API mancante, non valida, revocata o scaduta.
402
Il piano non include l'accesso API, oppure non è attivo un abbonamento.
403
La chiave non ha il permesso richiesto per questa operazione.
404
La risorsa non esiste, è stata eliminata o non appartiene alla tua officina.
500
Errore imprevisto del server. Il dettaglio finisce nei log, non nella risposta.
Richiesta
curl -X DELETE https://api.officina.it/v1/quotes/{id} \
  -H "Authorization: Bearer ofk_live_LA_TUA_CHIAVE" \
  -H "X-API-Version: 2026-10-31"
Risposta
{
  "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,
  "work_category_ids": [],
  "work_categories": [],
  "km": 61240,
  "expires_at": "2026-08-15",
  "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"
}
POST /v1/quotes/{id}/convert
Permesso richiesto quotes.write

Converti in scheda lavoro

Genera una scheda lavoro dal preventivo, copiandone righe e totali, e collega i due documenti (worksheet_id sul preventivo). Attenzione: la risposta è la scheda lavoro creata, non il preventivo — l'oggetto restituito ha object: worksheet. La conversione è una sola volta: un preventivo già convertito risponde 409.
dalla revisione 2026-10-28

Corpo facoltativo {"mark_accepted": false}: crea la scheda senza cambiare lo stato del preventivo. Senza corpo, o con true, il preventivo diventa accepted come prima.

dalla revisione 2026-10-03

Il documento creato eredita l'elenco completo delle categorie, in ordine; la risposta include work_category_ids e work_categories.

Risposte
201
La scheda lavoro generata dal preventivo.
401
Chiave API mancante, non valida, revocata o scaduta.
402
Il piano non include l'accesso API, oppure non è attivo un abbonamento.
403
La chiave non ha il permesso richiesto per questa operazione.
404
La risorsa non esiste, è stata eliminata o non appartiene alla tua officina.
415
Il Content-Type deve essere application/json e Accept deve accettare JSON.
422
Un campo della richiesta manca o non è valido: «param» indica quale.
500
Errore imprevisto del server. Il dettaglio finisce nei log, non nella risposta.
Richiesta
curl -X POST https://api.officina.it/v1/quotes/{id}/convert \
  -H "Authorization: Bearer ofk_live_LA_TUA_CHIAVE" \
  -H "X-API-Version: 2026-10-31" \
  -H "Content-Type: application/json" \
  -d '{
  "mark_accepted": false
}'
Risposta
{
  "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"
}
POST /v1/quotes/{id}/line-items/batch
Permesso richiesto quotes.write

Modifica le righe in blocco

Aggiunge, aggiorna, rimuove e riordina le righe in una sola richiesta, applicata come singola transazione: se una operazione fallisce non viene applicata nessuna. I totali del preventivo vengono ricalcolati e restituiti.
dalla revisione 2026-10-22

If-Match obbligatorio da questa revisione: senza l'header la richiesta è rifiutata con 428 precondition_required (If-Match: * scrive comunque). Con una version superata risponde 409.

Risposte
200
Il preventivo con le righe aggiornate e i totali ricalcolati.
400
Corpo della richiesta non leggibile o JSON non valido.
401
Chiave API mancante, non valida, revocata o scaduta.
402
Il piano non include l'accesso API, oppure non è attivo un abbonamento.
403
La chiave non ha il permesso richiesto per questa operazione.
404
La risorsa non esiste, è stata eliminata o non appartiene alla tua officina.
409
Il documento è stato salvato nel frattempo (la `version` di `If-Match` è superata): ricaricalo e riprova. Nulla è stato salvato.
415
Il Content-Type deve essere application/json e Accept deve accettare JSON.
422
Un campo della richiesta manca o non è valido: «param» indica quale.
428
Manca l'header `If-Match` (obbligatorio dalla revisione 2026-10-22).
500
Errore imprevisto del server. Il dettaglio finisce nei log, non nella risposta.
Richiesta
curl -X POST https://api.officina.it/v1/quotes/{id}/line-items/batch \
  -H "Authorization: Bearer ofk_live_LA_TUA_CHIAVE" \
  -H "X-API-Version: 2026-10-31" \
  -H "Content-Type: application/json" \
  -d '{
  "add": [
    {
      "description": "Estimated labour",
      "quantity": 2,
      "unit_type": "pz",
      "unit_price": 35,
      "unit_discount_rate": 0,
      "vat_code": "22"
    }
  ]
}'
Risposta
{
  "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,
  "work_category_ids": [],
  "work_categories": [],
  "km": 61240,
  "expires_at": "2026-08-15",
  "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"
    }
  ]
}
POST /v1/quotes/{id}/transitions
Permesso richiesto quotes.write

Cambia stato al preventivo

Porta il preventivo in waiting (segnato come inviato), accepted o rejected; una bozza può passare a ciascuno dei tre. È il modo corretto di registrare l'esito: non impostare status via PATCH. Accettare un preventivo non crea da solo la scheda lavoro — per quello serve POST /v1/quotes/{id}/convert.
dalla revisione 2026-10-28

Da draft si può andare a waiting (segna come inviato), accepted o rejected. Non si torna a draft.

Risposte
200
Il preventivo dopo la transizione.
400
Corpo della richiesta non leggibile o JSON non valido.
401
Chiave API mancante, non valida, revocata o scaduta.
402
Il piano non include l'accesso API, oppure non è attivo un abbonamento.
403
La chiave non ha il permesso richiesto per questa operazione.
404
La risorsa non esiste, è stata eliminata o non appartiene alla tua officina.
415
Il Content-Type deve essere application/json e Accept deve accettare JSON.
422
Un campo della richiesta manca o non è valido: «param» indica quale.
500
Errore imprevisto del server. Il dettaglio finisce nei log, non nella risposta.
Richiesta
curl -X POST https://api.officina.it/v1/quotes/{id}/transitions \
  -H "Authorization: Bearer ofk_live_LA_TUA_CHIAVE" \
  -H "X-API-Version: 2026-10-31" \
  -H "Content-Type: application/json" \
  -d '{
  "action": "accepted"
}'
Risposta
{
  "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,
  "work_category_ids": [],
  "work_categories": [],
  "km": 61240,
  "expires_at": "2026-08-15",
  "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"
    }
  ]
}
GET /v1/quotes/{id}/waste-disposal
Permesso richiesto quotes.read

Anteprima smaltimento rifiuti

Dice se lo smaltimento rifiuti si può applicare al preventivo e quanto vale (importo netto, prima dell'IVA), secondo le impostazioni waste_disposal_* dell'officina. reason spiega perché non è applicabile: disabled, already_applied, nothing_to_charge.
dalla revisione 2026-10-06

Nuovo endpoint.

Risposte
200
L'anteprima dell'addebito.
401
Chiave API mancante, non valida, revocata o scaduta.
402
Il piano non include l'accesso API, oppure non è attivo un abbonamento.
403
La chiave non ha il permesso richiesto per questa operazione.
404
La risorsa non esiste, è stata eliminata o non appartiene alla tua officina.
500
Errore imprevisto del server. Il dettaglio finisce nei log, non nella risposta.
Richiesta
curl https://api.officina.it/v1/quotes/{id}/waste-disposal \
  -H "Authorization: Bearer ofk_live_LA_TUA_CHIAVE" \
  -H "X-API-Version: 2026-10-31"
Risposta
{
  "object": "waste_disposal_preview",
  "applicable": true,
  "reason": null,
  "rate": 1.2,
  "base": "articles",
  "description": "Smaltimento rifiuti",
  "amount": 2.5
}
POST /v1/quotes/{id}/waste-disposal
Permesso richiesto quotes.write

Applica smaltimento rifiuti

Aggiunge la riga di smaltimento rifiuti (quantità 1, unità lav, aliquota IVA predefinita dell'officina, in fondo al documento), ricalcola i totali e restituisce il documento aggiornato. La riga si aggiunge una sola volta: se non è applicabile la risposta è 409 waste_disposal_not_applicable con il motivo in error.reason.
dalla revisione 2026-10-06

Nuovo endpoint.

Risposte
200
Il documento con la riga aggiunta e i totali ricalcolati.
401
Chiave API mancante, non valida, revocata o scaduta.
402
Il piano non include l'accesso API, oppure non è attivo un abbonamento.
403
La chiave non ha il permesso richiesto per questa operazione.
404
La risorsa non esiste, è stata eliminata o non appartiene alla tua officina.
409
Non applicabile (motivo in `error.reason`) oppure scheda bloccata.
500
Errore imprevisto del server. Il dettaglio finisce nei log, non nella risposta.
Richiesta
curl -X POST https://api.officina.it/v1/quotes/{id}/waste-disposal \
  -H "Authorization: Bearer ofk_live_LA_TUA_CHIAVE" \
  -H "X-API-Version: 2026-10-31"