Torna alla guida

API Bill Alps: convenzioni ed errori

Ciò che vale su tutte le rotte: busta, paginazione, date, corpi stretti, errori, utilizzo, e cosa fa un reset del sandbox alle vostre chiavi.

Sviluppatori6 min di lettura
Indice+

Le altre tre guide seguono dei percorsi; questa raccoglie le regole che si applicano a ogni chiamata, da tenere a portata di mano mentre scrivete il vostro 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 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:

{ "data": [ ... ], "nextCursor": "..." }

Tre liste non sono paginate e rispondono con un array semplice sotto data: GET /companies, GET /mandates e GET /companies/{companyId}/invoice-templates.

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. Un'eccezione: le assenze memorizzano e restituiscono stringhe YYYY-MM-DD, perché è il loro ordine di ordinamento.

Gli identificativi sono stringhe opache. 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.

4. Corpi stretti

  • I corpi delle rotte account, società, cliente, fattura, preventivo, ricorrenza e modello sono stretti: ogni chiave sconosciuta è rifiutata.
  • Un corpo è sempre richiesto su POST e PATCH ({} è una patch vuota valida; un corpo vuoto è INVALID_JSON), salvo sulle azioni senza corpo come POST …/default; DELETE non ne prende.
  • Oltre 1 MB, il corpo è rifiutato: 413 PAYLOAD_TOO_LARGE.
  • Cinque rotte prendono byte grezzi anziché JSON: il giustificativo di una spesa, l'estrazione IA e l'allegato IA (PDF, PNG, JPEG o WebP, 10 MB), più PUT …/logo su una società e su un modello di fattura (solo PNG o JPEG, 2 MB). Il Content-Type dichiara il formato e il corpo è il file; tutto il resto viaggia nella query.
  • Una chiave di query sconosciuta è un 400 VALIDATION_ERROR su ogni rotta che dichiara un contratto di query; GET /me e GET /companies ignorano il resto, senza che sia una promessa.
  • PUT su un'assenza è una sostituzione completa: i campi facoltativi omessi vengono cancellati.

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" } }

Solo VALIDATION_ERROR (con issues) e INVALID_TRANSITION aggiungono un message; ogni altro codice risponde da solo. Diramate su error.code, mai sulla prosa. 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 girano in quest'ordine: chiave e proprietario (401), visibilità della società (404), poi società archiviatapianomodulopermesso (403). Una società di cui il titolare della chiave non è membro è indistinguibile da una inesistente.

  • 401 INVALID_API_KEY: chiave assente, malformata, sconosciuta o revocata; proprietario bannato o eliminato.
  • 403 PLAN_NOT_ALLOWED, 403 COMPANY_ARCHIVED, 403 ACCESS_DENIED: piano, archiviazione, permesso o ruolo mancanti.
  • 403 INVOICING_MODULE_DISABLED, 403 PAYROLL_MODULE_DISABLED, 403 EXPENSES_MODULE_DISABLED, 403 FINANCIAL_STATEMENTS_MODULE_DISABLED: modulo disattivato sulla società.
  • 403 *_LIMIT_REACHED: quota del piano raggiunta (clienti, fatture, ricorrenze, dipendenti, buste, spese).
  • 400 BATCH_SIZE_INVALID: lotto fuori da 1 a 100 elementi.
  • 409 INVALID_TRANSITION, 409 PAYSLIP_ALREADY_EXISTS, 409 ABSENCE_OVERLAPS_EXISTING, 409 QUOTE_NOT_EDITABLE: conflitto con lo stato attuale.
  • 422 MODIFICATION_REASON_REQUIRED, 422 CLIENT_EMAIL_MISSING, 422 PERIOD_FISCAL_YEAR_MISMATCH: richiesta valida ma inammissibile in questo contesto.
  • 503 PDF_UNAVAILABLE, 502 EMAIL_SEND_FAILED, 500 INTERNAL_ERROR: riprovate.

La lista completa dei codici è l'enumerazione pubblicata nella Swagger UI e in openapi.json del vostro deployment.

6. Limiti e utilizzo

Non c'è ancora alcuna limitazione di frequenza. Le chiamate sono contate, non frenate: un contatore per chiave, endpoint, società interessata e giorno UTC, visibile in AccountChiavi APIUtilizzo. Le richieste respinte dall'autenticazione non sono contate; un 400 su una chiamata autorizzata sì.

Siate comunque un buon vicino: scorrete le liste invece di interrogarle in loop, e arretrate su 500 o 503. I due lotti non si comportano allo stesso modo: POST …/payslips/batch non è atomico e restituisce un risultato per elemento, POST …/bank-transactions/batch è atomico, tutto o niente.

7. Sandbox e reset

Il sandbox non scrive mai in produzione. E-mail, Stripe e inviti Clerk vi sono inerti, la registrazione è chiusa, i loghi non vengono copiati. Tutto il resto gira, compresi i job pianificati: le fatture ricorrenti vengono generate e le buste paga calcolate sui dati copiati.

Ritorno ai percorsi: Iniziare con l'API, Fatturare con l'API, Paghe, spese e contabilità con l'API. Ogni campo, ogni risposta e l'enumerazione completa dei codici sono nella Swagger UI del vostro deployment (…/api/docs).