Questa guida copre tutto ciò che non è fatturazione. Stessa chiave, stesso URL di base di Iniziare con l'API:
BASE=https://<deployment>.convex.site/api/v1
KEY=ba_...1. Due modi di indirizzare una società
Le rotte paghe sono indirizzate per società come tutte le altre risorse: /companies/{companyId}/employees e …/payslips. La stessa chiave fa le paghe di tutte le società a cui avete accesso — prendete il companyId da GET /companies.
Tutto il resto è indirizzato esplicitamente: assenze, spese, transazioni bancarie e rendiconti vivono sotto /companies/{companyId}/…, e una stessa chiave vi serve per tutte le società accessibili, a condizione che il modulo corrispondente sia attivo.
COMPANY=<companyId>2. Dipendenti
curl -H "Authorization: Bearer $KEY" $BASE/companies/$COMPANY/employeesLa lista è paginata (?limit, ?cursor, ordine di creazione) e restituisce ogni dipendente con identità, indirizzo, contratto, salario e tassi assicurativi. La creazione riprende l'insieme di campi del modulo dell'app:
curl -X POST -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
-d '{
"firstName": "Anna", "lastName": "Muster",
"address": { "kind": "swiss", "country": "CH", "street": "Rue du Lac", "streetNumber": "1",
"postalCode": "1700", "city": "Fribourg", "canton": "FR", "communeBfs": 2196 },
"permitType": "swiss", "contractType": "cdi", "maritalStatus": "single",
"grossMonthlySalary": 6500, "workPercentage": 100,
"startDate": "2026-01-01", "vacationWeeks": 5,
"thirteenthSalaryEnabled": true,
"language": "de"
}' $BASE/companies/$COMPANY/employeesLa risposta 201 contiene id e slug. Il campo facoltativo language (fr, de, en, it) è la lingua della busta paga: etichette memorizzate, PDF ed e-mail di invio; omesso, la busta segue la lingua del proprietario dell'account. Una modifica parziale ricalcola le buste non inviate del dipendente, come una modifica a schermo:
curl -X PATCH -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
-d '{ "grossMonthlySalary": 6800 }' $BASE/companies/$COMPANY/employees/<employeeId>Da prevedere: 403 EMPLOYEE_LIMIT_REACHED quando la quota dipendenti del piano è raggiunta, 422 END_DATE_REQUIRED_FOR_CDD per un contratto a tempo determinato senza data di fine, 422 END_DATE_BEFORE_START_DATE se precede l'inizio.
3. Buste paga
curl -H "Authorization: Bearer $KEY" "$BASE/companies/$COMPANY/payslips?year=2026&month=7"
curl -H "Authorization: Bearer $KEY" "$BASE/companies/$COMPANY/payslips?employeeId=<employeeId>"Lista paginata, dal più recente. year e month vanno sempre insieme (uno senza l'altro è un 400 VALIDATION_ERROR); employeeId filtra per persona — una pagina filtrata può essere corta, scorrete finché nextCursor è null. La creazione calcola la busta con tutti i controlli dell'app:
curl -X POST -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
-d '{ "employeeId": "<employeeId>", "year": 2026, "month": 7, "canton": "FR" }' \
$BASE/companies/$COMPANY/payslipsFacoltativo: variableItems (bonus, straordinari, righe libere) e overrides. Risposte da prevedere: 409 PAYSLIP_ALREADY_EXISTS se il periodo esiste già per quel dipendente, 422 PAYSLIP_PERIOD_OUTSIDE_EMPLOYMENT fuori dalla finestra di impiego, 422 MISSING_IS_BAREME quando nessuna tariffa dell'imposta alla fonte è caricata per quel cantone e quell'anno, 403 PAYSLIP_LIMIT_REACHED esaurita la quota mensile.
Per un mese intero, il lotto accetta da 1 a 100 buste e non è atomico: ogni elemento è elaborato da solo, e la risposta riporta l'esito di ciascuno. Un errore a metà lotto non annulla le buste già create.
curl -X POST -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
-d '{ "payslips": [
{ "employeeId": "<id1>", "year": 2026, "month": 7, "canton": "FR" },
{ "employeeId": "<id2>", "year": 2026, "month": 7, "canton": "FR" }
] }' $BASE/companies/$COMPANY/payslips/batch{
"data": {
"results": [
{ "index": 0, "status": "created", "id": "..." },
{ "index": 1, "status": "error", "code": "PAYSLIP_ALREADY_EXISTS" }
]
}
}Il PDF si scarica come quello di una fattura, con -L per seguire il redirect 302 verso un URL valido 15 minuti; il primo download di una busta può richiedere qualche secondo, il tempo di generarla.
curl -L -H "Authorization: Bearer $KEY" -o payslip.pdf $BASE/companies/$COMPANY/payslips/<payslipId>/pdf4. Assenze
Le assenze sono indirizzate per società (/companies/{companyId}/absences, modulo paghe), a differenza dei dipendenti. Dichiarare una malattia al 50 % su due settimane:
curl -X POST -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
-d '{ "employeeId": "<employeeId>", "motif": "maladie",
"startDate": "2026-03-02", "endDate": "2026-03-13",
"incapacityPercent": 50 }' \
$BASE/companies/$COMPANY/absencesemployeeId, motif e startDate sono obbligatori. motif vale maladie, accident, apg_maternite, apg_paternite, apg_adoption, apg_prise_en_charge, apg_service, maintien_salaire, conge_court o conge_non_paye. Le date qui sono stringhe YYYY-MM-DD, non millisecondi, perché è così che le assenze sono memorizzate e ordinate; un'assenza senza endDate è in corso.
dailyAmountOverride: indennità giornaliera comunicata dall'assicuratore, che scavalca sia il tasso sia il tetto legale.waitingDaysOverride: giorni di attesa della polizza malattia.ratePercentOverride: tasso da 0 a 100, predefinito 80 o quello configurato per il dipendente.takenAsIsolatedDays: paternità o adozione presa in giorni isolati (integrazione IPG 7 per 5).incapacityPercent: da 1 a 100, predefinito 100; enotes.
La risposta 201 restituisce l'assenza, sempre in stato declared. 409 ABSENCE_OVERLAPS_EXISTING segnala che un'altra assenza dello stesso dipendente copre già alcuni di quei giorni: una sovrapposizione raddoppierebbe le righe di trattenuta e indennità, dividete i periodi.
curl -X PUT -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
-d '{ "motif": "maladie", "startDate": "2026-03-02", "endDate": "2026-03-20" }' \
$BASE/companies/$COMPANY/absences/<absenceId>Lo stato ha la propria sotto-rotta: declaredapprovedpaid, un semplice marcatore di seguito, qualsiasi destinazione è accettata da qualsiasi stato.
curl -X POST -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
-d '{ "status": "approved" }' \
$BASE/companies/$COMPANY/absences/<absenceId>/statusNon c'è volutamente alcun filtro per periodo: un'assenza in corso non ha fine, e un filtro per mese la farebbe sparire. Scorrete l'anno e filtrate lato client, o restringete per employeeId. Eliminare un'assenza non ritira le righe già contabilizzate su una busta paga; solo un ricalcolo della busta lo fa.
5. Spese
curl -X POST -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
-d '{ "vendorName": "Swisscom", "status": "toPay", "documentDate": "2026-02-28",
"currency": "CHF", "total": 129.9, "category": "telecom" }' \
$BASE/companies/$COMPANY/expensesvendorName, status, documentDate, currency, total e category sono obbligatori; la ripartizione IVA (subtotal, vatRate, vatAmount) e gli identificativi del fornitore sono facoltativi. status vale toPay o paid. category è una delle 16 categorie fisse: rent, insurance, goodsPurchases, subcontracting, officeSupplies, itSoftware, telecom, vehicle, travel, meals, marketing, professionalFees, utilities, bankFees, training, other.
year è derivato da documentDate, e paidDate segue status: impostato a adesso quando la spesa è creata pagata senza data, cancellato appena lo stato non è più paid. Elencare un anno, poi segnare una spesa come pagata:
curl -H "Authorization: Bearer $KEY" "$BASE/companies/$COMPANY/expenses?year=2026"
curl -X PATCH -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
-d '{ "status": "paid" }' \
$BASE/companies/$COMPANY/expenses/<expenseId>Il giustificativo memorizzato è esposto in sola lettura, con lo stesso redirect 302 dei PDF; una spesa creata senza giustificativo (hasDocument: false) risponde 404:
curl -L -H "Authorization: Bearer $KEY" -o receipt.pdf \
$BASE/companies/$COMPANY/expenses/<expenseId>/documentFuori perimetro in v1: il caricamento di un giustificativo (nessun endpoint multipart), l'estrazione via IA, e le bozze di importazione da Drive in attesa di convalida, invisibili qui. Codici da prevedere: 403 SUPPLIER_BILL_LIMIT_REACHED esaurita la quota mensile di spese, 403 CURRENCY_PRO_ONLY per una valuta diversa da CHF su un piano Free.
6. Transazioni bancarie
Non c'è un POST singolo: l'app crea transazioni solo a lotti, un file importato è un lotto, e il lotto è il punto d'ingresso.
curl -X POST -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
-d '{ "fileName": "ubs-2026-01.csv",
"transactions": [
{ "date": "2026-01-15", "amount": -1250, "currency": "CHF",
"label": "Loyer janvier", "bankAccount": "CH93 0076 2011 6238 5295 7",
"category": "rent" }
] }' \
$BASE/companies/$COMPANY/bank-transactions/batchtransactions contiene da 1 a 100 righe (altrimenti 400 BATCH_SIZE_INVALID), ciascuna almeno con date, amount, currency, label e bankAccount. amount è con segno: un incasso è positivo, un addebito negativo. Il lotto è un'unica transazione di database, tutto viene scritto o nulla. importBatchId raggruppa le righe di uno stesso file; omesso, viene generato e la risposta ve lo restituisce. Una category inviata è registrata come scelta manuale che l'euristica di importazione non sovrascriverà mai.
{ "data": { "inserted": 1, "importBatchId": "9f1c…" } }curl -H "Authorization: Bearer $KEY" "$BASE/companies/$COMPANY/bank-transactions?year=2026"
curl -X PATCH -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
-d '{ "category": "insurance", "color": "#3c8fc3" }' \
$BASE/companies/$COMPANY/bank-transactions/<transactionId>year è l'anno civile della data di registrazione; un esercizio che non chiude a dicembre ne copre due, lasciate da parte il filtro per scorrere tutto lo storico. PATCH accetta category (segnata come manuale) e color, un'etichetta #rrggbb o "" per cancellarla; DELETE risponde 204.
7. Rendiconto finanziario
Il rendiconto finanziario annuale è una risorsa per società e per esercizio: fiscalYear è l'anno di chiusura (l'esercizio 2025/26 di una società che chiude a giugno è 2026) e funge da identificativo, da cui una coppia GET/PUT sull'anno invece di una collezione.
curl -H "Authorization: Bearer $KEY" \
$BASE/companies/$COMPANY/financial-statements/2026La lettura restituisce l'input memorizzato, l'istantanea ratesUsed e, una volta che il generatore dell'app l'ha prodotto, il documento calcolato sotto snapshot. 404 se nulla è memorizzato per quell'anno. La scrittura è un upsert su (società, esercizio):
curl -X PUT -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
-d '{ "input": { "accounts": [ { "id": "CH93…", "currency": "CHF", "openingBalance": 12000 } ],
"manual": { "tradeReceivables": 0, "otherShortTermReceivables": 0,
"fixedAssets": 0, "ownerCurrentAccountBalance": 0,
"tradePayables": 0, "otherShortTermDebts": 0,
"avsBalance": 0, "lppBalance": 0, "accruedLiabilities": 0,
"taxProvision": 0, "shareCapital": 20000, "legalReserve": 0,
"retainedEarnings": 0, "depreciation": 0,
"proposedDividend": 0, "proposedReserveAllocation": 0,
"employeeCount": 2 },
"closingRates": { "EUR": 0.93 } } }' \
$BASE/companies/$COMPANY/financial-statements/2026Il corpo porta l'input del modulo di generazione: i conti bancari confermati con il saldo di apertura, le voci inserite a mano (manual), i tassi di cambio di chiusura e un period facoltativo. Lo snapshot è un dato derivato, prodotto dal generatore, mai nella richiesta: un PUT che lo omette lascia intatto il documento memorizzato. period serve solo per un esercizio che non è l'anno civile, e il suo to deve cadere in fiscalYear (altrimenti 422 PERIOD_FISCAL_YEAR_MISMATCH).
8. Mandati fiduciari
Per un fiduciario, /mandates è una rotta a livello di chiave, senza piano né modulo: elenca le società clienti che possedete. mandateId è semplicemente il companyId del mandato. Creare un mandato crea una società:
curl -X POST -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
-d '{ "name": "Muster AG" }' $BASE/mandatesDa quel momento, ogni rotta /companies/{companyId}/… si applica al mandato con quello stesso identificativo — dipendenti, buste paga e assenze compresi: la stessa chiave elenca i dipendenti di un mandato e ne fa le paghe.
Per le regole valide ovunque (paginazione, date, corpi stretti, codici di errore, sandbox), leggete Convenzioni ed errori; il percorso di fatturazione è in Fatturare con l'API. Il dettaglio di ogni campo è nella Swagger UI del vostro deployment (…/api/docs).