Erreurs et limites
Format des erreurs de l'API NovaFisko, codes HTTP, codes machine, limites de débit par route et conduite à tenir après un 429.
L'API utilise les codes HTTP standards et renvoie toujours un corps JSON en cas d'erreur.
Format d'une erreur
{
"message": "Le plafond de 5 dossiers de votre licence est atteint.",
"code": "company_limit_reached",
"errors": {
"name": ["Le plafond de 5 dossiers de votre licence est atteint."]
}
}
| Champ | Présence | Description |
|---|---|---|
message |
toujours | Texte lisible, traduit dans la langue de l'utilisateur |
code |
quand l'erreur est métier | Identifiant stable, en anglais, à utiliser dans votre code |
errors |
erreurs de validation | Objet champ → liste de messages. Les champs imbriqués utilisent la notation pointée : lines.0.account_id |
Certaines erreurs ajoutent des champs propres, par exemple required_ability, server_version et server_data, ou blocker_codes pour Peppol.
Traitez d'abord le statut HTTP, puis code s'il est présent. N'analysez jamais le texte de message.
Codes HTTP
| Statut | Signification | Conduite à tenir |
|---|---|---|
200 |
Succès | |
201 |
Ressource créée | |
202 |
Demande acceptée, traitement immédiat ou différé (test de webhook) | |
204 |
Succès sans corps (suppression, déconnexion) | |
401 |
Jeton absent, invalide, révoqué ou expiré | Se reconnecter ou changer de jeton |
403 |
Action interdite pour ce jeton ou ce rôle | Lire code |
404 |
Ressource inconnue, ou dossier auquel l'utilisateur n'a pas accès | Vérifier le public_token et les droits |
409 |
Conflit de version (X-Base-Version) |
Recharger, fusionner, renvoyer |
422 |
Données refusées (validation ou règle comptable) | Corriger selon errors et code |
429 |
Trop de requêtes | Attendre Retry-After secondes |
500 |
Erreur interne | Réessayer plus tard avec la même clé d'idempotence |
503 |
Service indisponible ou maintenance, toujours avec un code |
Réessayer avec un délai croissant |
Un dossier qui existe mais auquel vous n'avez pas accès répond 404 et non 403. C'est voulu : l'API ne révèle pas l'existence d'un dossier à qui n'y est pas autorisé.
Codes machine
Authentification et accès
code |
Statut | Signification |
|---|---|---|
token_ability_missing |
403 | Le jeton d'intégration n'a pas la capacité indiquée dans required_ability |
session_token_required |
403 | Route réservée aux jetons de session |
token_limit_reached |
422 | 50 jetons d'intégration par utilisateur au maximum |
webhook_limit_reached |
422 | 20 points de réception par cabinet au maximum |
webhook_url_refused |
422 | URL de webhook refusée (HTTPS requis, hôte privé ou non résolu) |
demo_mode |
403 | Effet externe bloqué dans le cabinet de démonstration |
demo_unavailable |
503 | Le cabinet de démonstration n'est pas disponible |
service_unavailable |
503 | Indisponibilité générale |
Licence et plafonds
code |
Statut | Signification |
|---|---|---|
company_limit_reached |
422 | Plafond de dossiers de la licence ou du plan atteint |
user_limit_reached |
422 | Plafond d'utilisateurs de la licence atteint |
invalid_key |
422 | Clé d'activation inconnue |
expired_key |
422 | Clé d'activation expirée |
exhausted_key |
422 | Clé déjà utilisée le nombre de fois autorisé |
key_reserved |
422 | Clé réservée à un autre cabinet |
already_active |
422 | Cette clé est déjà active sur le cabinet |
Concurrence et historique
code |
Statut | Signification |
|---|---|---|
conflict |
409 ou 422 | L'enregistrement a changé sur le serveur depuis votre lecture |
period_locked |
422 | Période verrouillée ou exercice clôturé |
vat_locked |
422 | Déclaration TVA validée ou déposée |
entry_immutable |
422 | Une écriture validée ne se modifie pas : il faut l'extourner |
already_reverted, already_reversed |
422 | Le retour en arrière ou l'extourne a déjà eu lieu |
in_use, booked |
422 | Suppression refusée : l'enregistrement est utilisé ou comptabilisé |
code_taken |
422 | Restauration refusée : le code a été repris entre-temps |
not_revertible, nothing_to_revert, operation_not_found |
422 ou 404 | Retour en arrière impossible |
force_forbidden, read_only |
403 | Rôle insuffisant |
Synchronisation
Ces codes apparaissent dans le résultat de chaque mutation de sync/push, pas comme statut HTTP.
code |
Signification |
|---|---|
locked |
Donnée comptable validée ou verrouillée côté serveur |
validation |
Données refusées, détail dans errors |
not_supported_offline |
Cette opération ne passe pas par sync/push |
not_found, forbidden, invalid_mutation, unknown_temp_id, unprocessable |
Mutation refusée définitivement |
server_error |
Erreur temporaire, retryable: true |
conflict_already_resolved, conflict_choice_unavailable |
Résolution de conflit refusée (422) |
Peppol
Les refus Peppol renvoient blocker_codes, une liste parmi : provider_not_configured, vat_number_missing, address_missing, email_missing, already_registered, managed_by_novadesko, registered_elsewhere, not_registered, provider_error.
{
"message": "Le dossier ne peut pas être inscrit sur Peppol.",
"errors": {"peppol": ["Le dossier ne peut pas être inscrit sur Peppol."]},
"blockers": ["L'adresse e-mail de contact est manquante."],
"blocker_codes": ["email_missing"],
"state": {"status": "none", "can_register": false}
}
Toutes les erreurs 422 ne portent pas de code. Une erreur de validation simple ne contient que message et errors. Prévoyez ce cas.
Erreur de validation
{
"message": "Le champ entry date est obligatoire. (et 1 erreur supplémentaire)",
"errors": {
"entry_date": ["Le champ entry date est obligatoire."],
"lines.1.account_id": ["Le champ lines.1.account_id est obligatoire quand lines.1.account n'est pas présent."]
}
}
Limites de débit
Les limites s'appliquent par minute. Elles sont comptées par utilisateur pour les routes authentifiées et par adresse IP pour les routes publiques. Les routes qui ne figurent pas dans ce tableau n'ont pas de limite propre.
| Route | Limite par minute |
|---|---|
POST auth/login |
20 |
POST auth/demo |
10 |
POST auth/tokens |
20 |
POST firms/{firm}/webhooks/{id}/test |
12 |
GET pricing, GET pricing/simulate |
120 |
POST firms/{firm}/licence/activate |
10 |
POST firms/{firm}/sync |
6 |
GET lookup/search, lookup/companies/{identifier}, lookup/vat |
60 |
GET lookup/cities, lookup/streets |
120 |
POST companies/{company}/apply-sheet, third-parties/{id}/apply-sheet |
30 |
POST document-imports |
30 |
POST document-imports/{id}/process, .../validate |
120 |
POST document-imports/{id}/retry |
60 |
POST documents/book-all |
5 |
POST bank-transactions/match |
10 |
POST bank-transactions/book-matched |
5 |
POST payments/sepa |
20 |
GET vies, POST third-parties/{id}/vies |
30 |
POST sync/novadesko |
10 |
POST integrations/{id}/test, .../sync |
12 |
POST peppol/register |
6 |
POST peppol/refresh |
12 |
GET peppol/lookup |
30 |
GET third-parties/{id}/peppol |
60 |
POST third-parties/peppol/check-all |
6 |
GET exports/{type} |
30 |
GET exports/{type}/preview-url |
60 |
GET exports/full-dossier/{fiscalYear} |
10 |
POST fiscal-years/{id}/year-end |
5 |
GET history/export |
30 |
Chaque réponse d'une route limitée indique votre consommation :
HTTP/1.1 200 OK
X-RateLimit-Limit: 30
X-RateLimit-Remaining: 27
Réponse 429
HTTP/1.1 429 Too Many Requests
Retry-After: 41
X-RateLimit-Limit: 30
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1791188421
Content-Type: application/json
{"message": "Too Many Attempts."}
Attendez le nombre de secondes indiqué par Retry-After avant de réessayer. X-RateLimit-Reset donne l'instant de remise à zéro en secondes Unix.
import time
import requests
def call(method, url, **kwargs):
for attempt in range(5):
response = requests.request(method, url, timeout=60, **kwargs)
if response.status_code == 429:
# Honour the server delay, never hammer the API
time.sleep(int(response.headers.get("Retry-After", "5")) + 1)
continue
if response.status_code in (500, 503):
time.sleep(2 ** attempt)
continue
return response
raise RuntimeError("NovaFisko API unavailable")
Quand vous réessayez une écriture après une erreur réseau, un 500 ou un 503, renvoyez le même X-Client-Mutation-Id. Sans cela, vous risquez de créer un doublon. Voir Idempotence.
Autres limites
| Limite | Valeur |
|---|---|
| Fichiers par dépôt dans l'importateur | 20 |
| Taille d'un fichier déposé | 25 Mo |
| Fichiers extraits d'une archive ZIP | 50 |
Mutations par appel sync/push |
200 |
Changements par appel sync/pull |
500 par défaut, 1000 au maximum |
| Éléments par page | 50 par défaut, 200 au maximum (500 pour les mouvements bancaires) |
| Durée d'une URL signée d'aperçu d'export | 15 minutes |