Torna alla guida

Fatturare con l'API: dal cliente alla fattura inviata

L'intero percorso di fatturazione in chiamate REST: cliente, modello, fattura, finalizzazione, invio, PDF, poi preventivi e fatture ricorrenti.

Sviluppatori7 min di lettura
Indice+

Questa guida rifà, in curl, ciò che la schermata di fatturazione fa con il mouse. Ogni chiamata usa la stessa chiave e lo stesso URL di base di Iniziare con l'API.

BASE=https://<deployment>.convex.site/api/v1
KEY=ba_...

1. Prerequisiti

Da GET /me, prendete l'id di una società con modules.invoicing attivo, e tenetelo a portata di mano:

COMPANY=<companyId>

2. Creare il cliente

curl -X POST -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
  -d '{ "name": "Muster AG", "email": "billing@muster.example",
        "address": { "kind": "swiss", "country": "CH", "street": "Rue du Lac",
                     "streetNumber": "12", "postalCode": "1700", "city": "Fribourg" } }' \
  $BASE/companies/$COMPANY/clients

La risposta 201 contiene il cliente con il suo id. Un cliente appartiene a una società: usare su una fattura il cliente di un'altra società risponde 403 ACCESS_DENIED. Si applica la quota clienti del piano (403 CLIENT_LIMIT_REACHED).

3. Scegliere un modello

curl -H "Authorization: Bearer $KEY" $BASE/companies/$COMPANY/invoice-templates

La lista dei modelli non è paginata e non prende parametri. Quello con isDefault: true è il modello su cui una fattura ripiega quando non precisate nulla: templateId è facoltativo alla creazione. Il logo si gestisce nell'app, hasLogo è in sola lettura.

4. Creare la fattura

curl -X POST -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
  -d '{ "clientId": "<clientId>", "lang": "fr", "currency": "CHF",
        "issueDate": "2026-03-01", "dueDate": "2026-03-31",
        "items": [{ "description": "Design", "quantity": 2, "unitPrice": 150, "vatRate": 8.1 }] }' \
  $BASE/companies/$COMPANY/invoices

clientId, lang, issueDate, dueDate e currency sono obbligatori; templateId, discount, shippingCosts, notes, paymentTerms, showQrBill e items sono facoltativi. Il numero viene dal contatore della società e la fattura parte come bozza (draft).

La creazione conta nella quota mensile di fatture (403 INVOICE_LIMIT_REACHED), e una valuta diversa da CHF richiede un piano a pagamento (403 CURRENCY_PRO_ONLY).

5. Finalizzare e inviare

curl -X POST -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
  -d '{ "event": "FINALIZE" }' $BASE/companies/$COMPANY/invoices/<invoiceId>/transition

curl -X POST -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
  -d '{ "kind": "invoice" }' $BASE/companies/$COMPANY/invoices/<invoiceId>/send

Il ciclo di vita è quello dell'app: draftready (FINALIZE)sentpaid (MARK_PAID, con un paidDate facoltativo). CANCEL annulla da draft, ready, sent o overdue; REOPEN riporta in bozza una fattura annullata. Ogni altra coppia risponde 409 INVALID_TRANSITION, con la coppia rifiutata in message.

L'invio non è una transizione ma POST …/send: kind: "invoice" solo da ready (altrimenti 409 BILL_EMAIL_INVALID_STATUS), kind: "reminder" solo da overdue. Il PDF è allegato e la mail parte nella lingua della fattura; un cliente senza indirizzo e-mail risponde 422 CLIENT_EMAIL_MISSING.

6. Scaricare il PDF

curl -L -H "Authorization: Bearer $KEY" \
  $BASE/companies/$COMPANY/invoices/<invoiceId>/pdf -o invoice.pdf

curl -L -H "Authorization: Bearer $KEY" \
  $BASE/companies/$COMPANY/invoices/<invoiceId>/qr-bill-pdf -o qr-bill.pdf

Entrambe le rotte rispondono 302 verso un URL di breve durata (15 minuti): seguite sempre il redirect con -L e non mettete mai il Location in cache. I file sono generati su richiesta, la prima chiamata dopo una modifica renderizza il documento. 503 PDF_UNAVAILABLE significa che il rendering è fallito: riprovate.

7. Modificare una fattura emessa

Una volta uscita da draft, una fattura cambia solo con un motivo: ogni modifica di intestazione o riga deve portare un modificationReason non vuoto, registrato nel giornale della fattura. Senza, la risposta è 422 MODIFICATION_REASON_REQUIRED e nulla viene scritto.

curl -X PATCH -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
  -d '{ "dueDate": "2026-04-30", "modificationReason": "Client asked for a longer term" }' \
  $BASE/companies/$COMPANY/invoices/<invoiceId>

Le righe hanno le proprie rotte (POST …/items, PATCH …/items/{itemId}, DELETE …/items/{itemId}), tutte restituiscono l'intera fattura con i totali ricalcolati. Poiché un DELETE non ha corpo, il motivo viaggia come parametro di query:

curl -X DELETE -H "Authorization: Bearer $KEY" \
  "$BASE/companies/$COMPANY/invoices/<invoiceId>/items/<itemId>?modificationReason=Line%20billed%20twice"

8. Preventivi e fatture ricorrenti

I preventivi (/companies/{companyId}/quotes) hanno la forma delle fatture, con una regola in più: solo una bozza si modifica. Un preventivo inviato risponde 409 QUOTE_NOT_EDITABLE; riportatelo in bozza con REVERT_TO_DRAFT o duplicatelo. I preventivi sono illimitati su tutti i piani, solo la conversione consuma una fattura della quota:

curl -X POST -H "Authorization: Bearer $KEY" $BASE/companies/$COMPANY/quotes/<quoteId>/convert

La conversione crea una fattura in bozza e risponde 201 con invoiceId e invoiceNumber. Un preventivo si converte una volta (409 QUOTE_ALREADY_CONVERTED), e un preventivo rifiutato o scaduto non si converte (409 QUOTE_NOT_CONVERTIBLE).

Una fattura ricorrente (/companies/{companyId}/recurring-invoices) non è un documento ma un'intestazione, delle righe e un orologio: startDate innesca la prossima esecuzione, endDate o maxOccurrences la fermano, e con autoSend ogni fattura generata è finalizzata e inviata al cliente. Non c'è una rotta per riga, PUT …/items sostituisce l'intero insieme:

curl -X PUT -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
  -d '{ "items": [
        { "description": "Abonnement", "quantity": 1, "unitPrice": 180, "vatRate": 8.1 },
        { "description": "Support", "quantity": 2, "unitPrice": 90, "vatRate": 8.1 }
      ] }' \
  $BASE/companies/$COMPANY/recurring-invoices/<scheduleId>/items

Per paghe e contabilità, continuate con Paghe, spese e contabilità con l'API; per paginazione, date e codici di errore, leggete Convenzioni ed errori. Il dettaglio di ogni campo è nella Swagger UI del vostro deployment (…/api/docs).