Le altre guide seguono dei percorsi; questa raccoglie le regole che valgono per ogni chiamata, da tenere a portata di mano mentre scrivi il tuo client.
BASE=https://<deployment>.convex.site/api/v1
KEY=ba_...1. La busta data
Ogni risposta riuscita avvolge il suo contenuto in data:
{ "data": [ ... ] }Due eccezioni senza corpo: 204 No Content sulle eliminazioni e su alcune azioni (PATCH …/payslips/{payslipId}, POST …/payslips/{payslipId}/send, POST …/quotes/{quoteId}/send, POST …/invoices/{invoiceId}/cancel-payment), e 302 sui download di PDF, giustificativi e loghi, da seguire con -L.
2. Paginazione
Le liste delle risorse di società sono paginate, dalla più recente alla più vecchia (i dipendenti seguono l'ordine di creazione):
{ "data": [ ... ], "nextCursor": "..." }Alcune liste non sono paginate e rispondono con un array semplice sotto data: GET /companies, GET /mandates, GET /companies/{companyId}/invoice-templates, GET …/payroll/holiday-variants e GET …/employees/{employeeId}/payslips-to-update. Anche POST …/payslips/pdf-batch è paginato, ma il suo cursore viaggia nel corpo, in cursor.
3. Date e identificativi
Le date sono accettate in millisecondi epoch o in YYYY-MM-DD e restituite in millisecondi epoch. I filtri from/to delimitano la data di emissione, estremi inclusi. Due eccezioni: le assenze e le date di un dipendente (startDate, endDate, dateOfBirth) sono stringhe YYYY-MM-DD, sia in ingresso sia in uscita.
Gli identificativi sono stringhe opache. Nel percorso, un identificativo sconosciuto, uno malformato e uno di un'altra società rispondono tutti 404 NOT_FOUND: i tre casi sono volutamente indistinguibili, perché nessun identificativo di un altro account trapeli. Nel corpo, un riferimento a un'altra società risponde in base alla rotta: 403 ACCESS_DENIED su una fattura o un preventivo, 422 CLIENT_NOT_FOUND o 422 TEMPLATE_NOT_FOUND su una ricorrenza, 422 EMPLOYEE_NOT_FOUND su un'assenza.
4. Corpi stretti
- I corpi JSON sono stretti: qualsiasi chiave sconosciuta risponde
400 VALIDATION_ERROR. Sulla creazione e sulla modifica di un dipendente e sulla creazione di una busta paga, il rifiuto proviene dal controllo lato server e indica il campo inmessage, senzaissues. I corpi di…/laa-periodse…/avs-choices, invece, ignorano le chiavi sconosciute. - Un corpo è sempre richiesto su
POST,PUTePATCH({}è una patch vuota valida; un corpo vuoto èINVALID_JSON), tranne sulle azioni senza corpo comePOST …/default,…/convert,…/cancel-paymento…/payslips/{payslipId}/send;DELETEnon ne accetta. Attenzione:POST …/payslips/{payslipId}/validaterichiede almeno{}. - Oltre 1 MB, il corpo è rifiutato:
413 PAYLOAD_TOO_LARGE. - Sette rotte accettano byte grezzi anziché un JSON.
POST …/expenses/{expenseId}/document,POST …/expense-drafts/extract,POST …/uploads/expenseePOST …/uploads/invoice-import: PDF, PNG, JPEG o WebP, 10 MB.POST …/uploads/invoice-source: PNG, JPEG o WebP, 8 MB.PUT …/logosu una società e su un modello di fattura: solo PNG o JPEG, 2 MB. IlContent-Typedichiara il formato e il corpo è il file; tutto il resto viaggia nella query. Un altro formato risponde415 UNSUPPORTED_MEDIA_TYPE, un corpo vuoto400 EMPTY_BODY. - Un caricamento
…/uploads/…restituisce unuploadIdvalido per un'ora. Oggi soloPOST …/expense-draftsne consuma uno: nessuna rotta pubblica accetta quelli diinvoice-sourceeinvoice-import. - Una chiave di query sconosciuta è un
400 VALIDATION_ERRORsu ogni rotta che dichiara un contratto di query; le altre, comeGET /meeGET /companies, ignorano il resto, senza che sia una promessa. PUTsu un'assenza è una sostituzione completa: i campi facoltativi omessi vengono cancellati.PUTsu una fattura o un preventivo sostituisce tutte le righe e richiedeexpectedVersion.
Un errore di validazione elenca ogni campo errato sotto issues, in percorsi puntati, indici di array compresi:
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Invalid request body",
"issues": [
{ "path": "subtotal", "message": "Unrecognized key \"subtotal\"" },
{ "path": "vatRates.0.rate", "message": "Too big: expected number to be <=100" }
]
}
}5. Errori
Gli errori portano un codice stabile, leggibile da una macchina:
{ "error": { "code": "PAYSLIP_ALREADY_EXISTS" } }VALIDATION_ERROR aggiunge issues, oppure un message per i campi controllati lato server. Anche INVALID_TRANSITION, BATCH_SIZE_INVALID e UNSUPPORTED_MEDIA_TYPE aggiungono un message, e PAYROLL_CUMULATIVE_INCOMPLETE un details.months; ogni altro codice risponde da solo. Basa la tua logica su error.code, mai sul testo. Questa busta vale su ogni percorso sotto /api/v1/, compreso un percorso sconosciuto (404 NOT_FOUND) e un metodo non supportato (405 METHOD_NOT_ALLOWED, con un header Allow).
I controlli vengono eseguiti in quest'ordine: percorso e metodo (404, 405), dimensione del corpo (413), chiave e proprietario (401), visibilità della società (404), poi società archiviatapianomodulopermesso (403). Seguono la validazione del corpo e della query (400) e poi le regole di business. Una società di cui chi detiene la chiave non è membro è indistinguibile da una società inesistente.
400 INVALID_JSON,400 VALIDATION_ERROR: corpo assente o illeggibile, campo non valido.400 BATCH_SIZE_INVALID:payslips/batchobank-transactions/batchfuori dall'intervallo da 1 a 100 elementi; gli altri lotti sono limitati dal loro schema e rispondonoVALIDATION_ERROR.401 INVALID_API_KEY: chiave assente, malformata, sconosciuta, revocata o scaduta; proprietario bannato o eliminato.402 SUBSCRIPTION_REQUIRED: creazione di un mandato senza un abbonamento fiduciario attivo.403 PLAN_NOT_ALLOWED,403 COMPANY_ARCHIVED,403 ACCESS_DENIED,403 ACCOUNT_DISABLED: piano, archiviazione, permesso o ruolo mancanti, account disattivato.403 INVOICING_MODULE_DISABLED,403 PAYROLL_MODULE_DISABLED,403 ACCOUNTING_MODULE_DISABLED,403 PRODUCT_CATALOG_DISABLED: modulo o catalogo disattivato sulla società.403 LEDGER_DISABLED,403 FINANCIAL_STATEMENTS_REPORTING_DISABLED: funzione chiusa sul deployment.403 *_LIMIT_REACHED: quota del piano raggiunta (clienti, prodotti, elenchi di prodotti, fatture, ricorrenze, dipendenti, buste paga, spese).403 EXPENSE_AI_QUOTA_REACHED,403 AI_QUOTA_REACHED: quote IA.403 CURRENCY_PRO_ONLY: valuta diversa dal CHF con i limiti Starter.403 FILE_ACCESS_DENIED,403 UPLOAD_EXPIRED:uploadIddi un altro utente, già utilizzato o scaduto.409 INVALID_TRANSITION,409 PAYSLIP_ALREADY_EXISTS,409 PAYSLIP_DECLARED,409 ABSENCE_OVERLAPS_EXISTING,409 QUOTE_NOT_EDITABLE,409 BILL_PAID_LOCKED,409 BILL_NOT_PAID,409 DOCUMENT_ALREADY_ATTACHED,409 EXPENSE_DRAFT_LIMIT_REACHED: conflitto con lo stato attuale.409 BILL_VERSION_CONFLICT,409 QUOTE_VERSION_CONFLICT: il documento è cambiato dalla tua ultima lettura.422 MODIFICATION_REASON_REQUIRED,422 REASON_REQUIRED,422 CLIENT_EMAIL_MISSING,422 EMPLOYEE_EMAIL_MISSING,422 CLASSIFICATION_ACCOUNT_REQUIRED,422 VALID_UNTIL_IN_PAST,422 SCAN_UNREADABLE: richiesta valida ma non ammissibile in questo contesto.429 AI_RATE_LIMITED: il modello di IA limita le chiamate, riprova.429 MANDATE_REQUEST_LIMIT_REACHED: troppe richieste di mandato.503 PDF_UNAVAILABLE,502 EMAIL_SEND_FAILED,502 AI_REQUEST_FAILED,500 INTERNAL_ERROR: riprova. Una sola eccezione: dopo502 QUOTE_EMAIL_SENT_TRANSITION_FAILEDil preventivo è già partito, non inviarlo di nuovo.
La Swagger UI e openapi.json del tuo deployment pubblicano i codici dichiarati, rotta per rotta. Restano possibili altri codici di business: prevedi sempre un caso predefinito su error.code.
6. Limiti e utilizzo
Non c'è ancora alcuna limitazione di frequenza. Le chiamate vengono contate, non frenate: un contatore per chiave, endpoint, società interessata e giorno UTC, visibile in Il mio accountChiavi APIUtilizzo. Le richieste respinte dall'autenticazione non vengono contate; un 400 su una chiamata autorizzata sì.
Comportati comunque da buon vicino: scorri le liste invece di interrogarle in loop e rallenta su 500 o 503. I lotti non si comportano tutti allo stesso modo. POST …/payslips/batch non è atomico e restituisce un risultato per elemento. POST …/bank-transactions/batch, POST …/clients/batch e POST …/{invoices,quotes,payslips}/bulk-delete sono atomici, tutto o niente. Le rotte bulk-* di spese e transazioni bancarie, e …/expense-drafts/accept e …/reject, elaborano ciò che possono e conteggiano il resto in skipped.
7. Sandbox e reset
La sandbox non scrive mai in produzione. E-mail, Stripe e inviti al team vi sono inerti, la registrazione è chiusa, i loghi non vengono copiati. Tutto il resto funziona, compresi i job pianificati: le fatture ricorrenti vengono generate e le buste paga calcolate sui dati copiati. Il libro mastro vi è aperto, a differenza della produzione, dove le sue rotte rispondono 403 LEDGER_DISABLED.
Torna ai percorsi: Iniziare con l'API, Fatturare con l'API, Paghe, spese e contabilità con l'API, Ciclo paghe con l'API. Ogni campo e ogni risposta si trovano nella Swagger UI del tuo deployment (…/api/docs).