Retour au guide

API Bill Alps : conventions et erreurs

Ce qui vaut sur toutes les routes : enveloppe, pagination, dates, corps stricts, erreurs, usage, et ce que fait une réinitialisation du sandbox à vos clés.

Développeurs6 min de lecture
Table des matières+

Les trois autres guides déroulent des parcours ; celui-ci rassemble les règles qui s'appliquent à chaque appel, à garder sous la main pendant que vous écrivez votre client.

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

1. L'enveloppe data

Toute réponse réussie enveloppe sa charge utile dans data :

{ "data": [ ... ] }

Deux exceptions sans corps : 204 No Content sur les suppressions, et 302 sur les téléchargements de PDF, de justificatifs et de logos, à suivre avec -L.

2. Pagination

Les listes des ressources de société sont paginées, du plus récent au plus ancien :

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

Trois listes ne sont pas paginées et répondent un tableau brut sous data : GET /companies, GET /mandates et GET /companies/{companyId}/invoice-templates.

3. Dates et identifiants

Les dates sont acceptées en millisecondes epoch ou en YYYY-MM-DD, et renvoyées en millisecondes epoch. Les filtres from/to bornent la date d'émission, bornes incluses. Une exception : les absences stockent et renvoient des chaînes YYYY-MM-DD, parce que c'est leur ordre de tri.

Les identifiants sont des chaînes opaques. Un identifiant inconnu, un identifiant malformé et un identifiant d'une autre société répondent tous 404 NOT_FOUND : les trois cas sont volontairement indiscernables, pour qu'aucun identifiant d'un autre compte ne fuite.

4. Corps stricts

  • Les corps des routes compte, société, client, facture, devis, récurrence et modèle sont stricts : toute clé inconnue est refusée.
  • Un corps est toujours requis sur POST et PATCH ({} est un patch vide valide ; un corps vide est INVALID_JSON), sauf sur les actions sans corps comme POST …/default ; DELETE n'en prend pas.
  • Au-delà de 1 Mo, le corps est refusé : 413 PAYLOAD_TOO_LARGE.
  • Cinq routes prennent des octets bruts plutôt qu'un JSON : le justificatif d'une dépense, l'extraction IA et la pièce jointe IA (PDF, PNG, JPEG ou WebP, 10 Mo), puis PUT …/logo sur une société et sur un modèle de facture (PNG ou JPEG seulement, 2 Mo). Le Content-Type annonce le format, le corps est le fichier ; tout le reste passe par la requête.
  • Une clé de requête inconnue est un 400 VALIDATION_ERROR sur toute route qui déclare un contrat de requête ; GET /me et GET /companies ignorent le reste, sans que ce soit une promesse.
  • PUT sur une absence est un remplacement complet : les champs facultatifs omis sont effacés.

Un échec de validation liste chaque champ fautif sous issues, en chemins pointés, index de tableau compris :

{
  "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. Erreurs

Les erreurs portent un code stable, lisible par une machine :

{ "error": { "code": "PAYSLIP_ALREADY_EXISTS" } }

Seuls VALIDATION_ERROR (avec issues) et INVALID_TRANSITION ajoutent un message ; tout autre code répond seul. Branchez-vous sur error.code, jamais sur la prose. Cette enveloppe vaut sur tout chemin sous /api/v1/, y compris un chemin inconnu (404 NOT_FOUND) et une méthode non supportée (405 METHOD_NOT_ALLOWED, avec un en-tête Allow).

Les contrôles tournent dans cet ordre : clé et propriétaire (401), visibilité de la société (404), puis société archivéeplanmodulepermission (403). Une société dont le porteur de clé n'est pas membre est indiscernable d'une société inexistante.

  • 401 INVALID_API_KEY : clé absente, malformée, inconnue, révoquée ; propriétaire banni ou supprimé.
  • 403 PLAN_NOT_ALLOWED, 403 COMPANY_ARCHIVED, 403 ACCESS_DENIED : plan, archivage, permission ou rôle manquants.
  • 403 INVOICING_MODULE_DISABLED, 403 PAYROLL_MODULE_DISABLED, 403 EXPENSES_MODULE_DISABLED, 403 FINANCIAL_STATEMENTS_MODULE_DISABLED : module désactivé sur la société.
  • 403 *_LIMIT_REACHED : quota du plan atteint (clients, factures, récurrences, employés, fiches, dépenses).
  • 400 BATCH_SIZE_INVALID : lot hors de 1 à 100 éléments.
  • 409 INVALID_TRANSITION, 409 PAYSLIP_ALREADY_EXISTS, 409 ABSENCE_OVERLAPS_EXISTING, 409 QUOTE_NOT_EDITABLE : conflit avec l'état actuel.
  • 422 MODIFICATION_REASON_REQUIRED, 422 CLIENT_EMAIL_MISSING, 422 PERIOD_FISCAL_YEAR_MISMATCH : requête valide mais irrecevable dans ce contexte.
  • 503 PDF_UNAVAILABLE, 502 EMAIL_SEND_FAILED, 500 INTERNAL_ERROR : réessayez.

La liste complète des codes est l'énumération publiée dans la Swagger UI et dans openapi.json de votre déploiement.

6. Limites et utilisation

Il n'y a pas encore de limitation de débit. Les appels sont comptés, pas freinés : un compteur par clé, endpoint, société visée et jour UTC, visible dans CompteClés APIUtilisation. Les requêtes rejetées par l'authentification ne sont pas comptées ; un 400 sur un appel autorisé l'est.

Soyez un bon voisin malgré tout : paginez les listes plutôt que de les interroger en boucle, et reculez sur 500 ou 503. Les deux lots ne se comportent pas pareil : POST …/payslips/batch n'est pas atomique et rend un résultat par élément, POST …/bank-transactions/batch est atomique, tout ou rien.

7. Sandbox et réinitialisation

Le sandbox n'écrit jamais en production. E-mails, Stripe et invitations Clerk y sont inertes, l'inscription est fermée, les logos ne sont pas copiés. Tout le reste tourne, y compris les tâches planifiées : les factures récurrentes y sont générées et les fiches de salaire calculées sur les données copiées.

Retour aux parcours : Démarrer avec l'API, Facturer par l'API, Paie, dépenses et comptabilité par l'API. Chaque champ, chaque réponse et l'énumération complète des codes sont dans la Swagger UI de votre déploiement (…/api/docs).