Aller au contenu
Documentation
Français
Ouvrir l'application

Webhooks

Recevoir les événements NovaFisko sur votre serveur, créer et tester un point de réception, comprendre les tentatives de livraison et consulter le journal.

Un webhook est un appel HTTP que NovaFisko envoie à votre serveur quand un événement se produit : une facture importée, une écriture validée, une déclaration TVA validée. Vous n'avez plus à interroger l'API pour savoir s'il y a du nouveau.

Les webhooks se configurent au niveau du cabinet et couvrent tous ses dossiers. Seuls les administrateurs et les gestionnaires du cabinet peuvent les gérer.

Fonctionnement

  1. Vous enregistrez une URL et choisissez les événements à recevoir.
  2. NovaFisko vous remet un secret de signature, une seule fois.
  3. À chaque événement, NovaFisko met une livraison en file, puis envoie une requête POST signée à votre URL, au plus tard dans la minute.
  4. Votre serveur vérifie la signature et répond par un statut 2xx en moins de 10 secondes.
  5. En cas d'échec, NovaFisko réessaie avec un délai croissant.

Créer un point de réception

curl -X POST https://api.novafisko.com/v1/firms/Fd7hQ2mN8sXa/webhooks \
  -H "Authorization: Bearer $NOVAFISKO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://erp.atelier.example/hooks/novafisko",
    "description": "ERP Atelier, production",
    "events": ["document.imported", "document.booked", "entry.posted"],
    "is_active": true
  }'
Champ Obligatoire Description
url oui URL de réception, 500 caractères max. HTTPS obligatoire en production
description non Libellé libre, 190 caractères max
events oui Liste d'événements, ou ["*"] pour tous
is_active non true par défaut

Réponse 201 :

{
  "webhook": {
    "id": 14,
    "url": "https://erp.atelier.example/hooks/novafisko",
    "description": "ERP Atelier, production",
    "events": ["document.imported", "document.booked", "entry.posted"],
    "is_active": true,
    "secret_hint": "whsec_…x9Qd",
    "last_delivery_at": null,
    "last_status": null,
    "created_at": "2026-10-05T09:20:00+00:00",
    "updated_at": "2026-10-05T09:20:00+00:00"
  },
  "secret": "whsec_Zp3kV8nQ1bT6yR0mW4sH7cJ2dL9fA5gE1uXox9Qd"
}
Attention

Le secret n'est renvoyé qu'à la création. Enregistrez-le immédiatement dans votre gestionnaire de secrets. Les lectures suivantes ne montrent que secret_hint, ses quatre derniers caractères.

Contraintes sur l'URL

  • Le schéma doit être https.
  • L'URL ne doit pas contenir d'identifiants (https://user:pass@...).
  • L'hôte ne doit pas être localhost, ni se terminer par .local ou .internal, ni pointer vers une adresse privée, de bouclage ou réservée.
  • Les redirections ne sont pas suivies.

Une URL refusée répond 422 avec le code webhook_url_refused. Un cabinet peut enregistrer 20 points de réception au maximum (webhook_limit_reached).

Gérer les points de réception

Action Requête Réponse
Lister GET /v1/firms/{firm}/webhooks {"data": [...]}
Modifier PATCH /v1/firms/{firm}/webhooks/{id} {"webhook": {...}}
Supprimer DELETE /v1/firms/{firm}/webhooks/{id} 204
Tester POST /v1/firms/{firm}/webhooks/{id}/test 202 et la livraison
Journal GET /v1/firms/{firm}/webhooks/{id}/deliveries Liste paginée
Catalogue GET /v1/webhooks/events Liste des événements et descriptions

Mettre un point de réception en pause sans le supprimer :

curl -X PATCH https://api.novafisko.com/v1/firms/Fd7hQ2mN8sXa/webhooks/14 \
  -H "Authorization: Bearer $NOVAFISKO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"is_active": false}'

Un utilisateur membre du cabinet sans le rôle requis reçoit 403. Un utilisateur extérieur au cabinet reçoit 404.

La requête reçue

POST /hooks/novafisko HTTP/1.1
Host: erp.atelier.example
Content-Type: application/json
User-Agent: NovaFisko-Webhooks/1.0
X-Novafisko-Event: entry.posted
X-Novafisko-Delivery: 90412
X-Novafisko-Signature: t=1791191400,v1=5f2b8c1e0a7d4f6b9c3e2a1d8f7b6c5a4e3d2c1b0a9f8e7d6c5b4a3f2e1d0c9b

{"id":"evt_01jb6z3m8q2v7k4n5p9r0s1t2w","event":"entry.posted","api_version":"v1","created_at":"2026-10-05T09:23:20Z","firm":{"public_token":"Fd7hQ2mN8sXa","name":"Fiduciaire Exemple"},"company":{"public_token":"k3Jd9fPq2LmX","name":"Le Comptoir Montois SRL"},"data":{"id":1842,"reference":"ACH 2026/000212"}}
En-tête Contenu
X-Novafisko-Signature Horodatage et signature HMAC-SHA256. Voir Vérifier les signatures
X-Novafisko-Event Nom de l'événement
X-Novafisko-Delivery Identifiant de la livraison, identique d'une tentative à l'autre
User-Agent NovaFisko-Webhooks/1.0

Enveloppe

Tous les événements partagent la même enveloppe.

Champ Description
id Identifiant unique de l'événement, préfixé evt_. Servez-vous-en pour dédoublonner
event Nom de l'événement
api_version v1
created_at Date de l'événement, ISO 8601 en UTC
firm public_token et nom du cabinet
company public_token et nom du dossier, ou null pour un événement de cabinet
data Contenu propre à l'événement

Événements

Événement Déclencheur
document.imported Un document entre dans le dossier (importateur, Novadesko, Peppol)
document.booked Un document est comptabilisé
entry.posted Une écriture est validée
entry.reversed Une écriture est extournée
bank.transaction_imported Un mouvement bancaire est importé
vat.declaration_validated Une déclaration TVA est validée
fiscal_year.closed Un exercice est clôturé
peppol.status_changed Le statut Peppol du dossier change
third_party.created Un tiers est créé
third_party.updated Un tiers est modifié
webhook.test Envoi de test, à la demande

Les exemples ci-dessous montrent le champ data de chaque événement. L'enveloppe est omise.

document.imported et document.booked

{
  "id": 7741,
  "source": "novafisko",
  "origin": "novafisko",
  "type": "purchases",
  "direction": "purchase",
  "is_credit_note": false,
  "number": "F-2026-0918",
  "document_date": "2026-10-02",
  "due_date": "2026-11-01",
  "third_party_id": 41,
  "third_party_name": "Brasserie des Collines SA",
  "third_party_vat": "BE0999900134",
  "currency": "EUR",
  "amount_net": "1250.00",
  "amount_vat": "262.50",
  "amount_gross": "1512.50",
  "status": "pending",
  "journal_entry_id": null
}

Pour document.booked, status vaut booked et journal_entry_id porte l'identifiant de l'écriture.

entry.posted et entry.reversed

{
  "id": 1842,
  "reference": "ACH 2026/000212",
  "journal_code": "ACH",
  "fiscal_year_code": "2026",
  "number": 212,
  "entry_date": "2026-10-02",
  "due_date": "2026-11-01",
  "label": "Brasserie des Collines SA",
  "document_reference": "F-2026-0918",
  "status": "posted",
  "origin": "manual",
  "third_party_id": 41,
  "total_debit": "1512.50",
  "total_credit": "1512.50",
  "lines_count": 3,
  "reversed_entry_id": null,
  "reversed_by_entry_id": null
}

Pour entry.reversed, l'événement porte sur l'écriture d'origine : status vaut reversed et reversed_by_entry_id désigne l'extourne. L'extourne elle-même déclenche son propre entry.posted, avec reversed_entry_id renseigné. Les lignes ne sont pas incluses : lisez-les avec GET entries/{id}.

bank.transaction_imported

{
  "id": 30418,
  "source": "novadesko",
  "account_iban": "BE68539007547034",
  "value_date": "2026-10-03",
  "amount": "-1512.50",
  "currency": "EUR",
  "counterpart_name": "Brasserie des Collines SA",
  "counterpart_iban": "BE71096123456769",
  "communication": "+++091/8202/60018+++",
  "status": "pending"
}

vat.declaration_validated

{
  "id": 88,
  "period_type": "quarterly",
  "year": 2026,
  "period": 3,
  "label": "3T 2026",
  "starts_on": "2026-07-01",
  "ends_on": "2026-09-30",
  "amount_due": "4218.36",
  "amount_refund": "0.00",
  "status": "validated",
  "validated_at": "2026-10-05T09:31:02+00:00",
  "journal_entry_id": 1860
}

fiscal_year.closed

{
  "id": 6,
  "code": "2025",
  "starts_on": "2025-01-01",
  "ends_on": "2025-12-31",
  "closed_at": "2026-03-14T10:22:05+00:00"
}

peppol.status_changed

{
  "id": 57,
  "participant_id": "0208:0999900134",
  "full_id": "iso6523-actorid-upis::0208:0999900134",
  "status": "active",
  "previous_status": "pending",
  "incoming_enabled": true,
  "activated_at": "2026-10-05T09:02:11+00:00"
}

previous_status vaut null lors de la première inscription.

third_party.created et third_party.updated

{
  "id": 215,
  "type": "supplier",
  "code": "MARBOR",
  "name": "Maraîchers du Borinage SC",
  "vat_number": "BE0999900233",
  "enterprise_number": "0999.900.233",
  "country": "BE",
  "email": "info@maraichers-borinage.example",
  "peppol_identifier": "0208:0999900233",
  "peppol_registered": true,
  "changed": ["email", "phone"]
}

Le champ changed, présent uniquement pour third_party.updated, liste les champs modifiés. Les vérifications techniques (VIES, Peppol) ne déclenchent pas d'événement à elles seules.

webhook.test

{
  "message": "Test event sent from NovaFisko.",
  "webhook_id": 14
}

Pour cet événement, company vaut null.

Tentatives et délais

Votre serveur doit répondre par un statut 2xx en moins de 10 secondes. Toute autre réponse, un dépassement de délai ou une erreur de connexion compte comme un échec.

Tentative Délai après l'échec précédent
1 Immédiate, dans la minute qui suit l'événement
2 1 minute
3 2 minutes
4 4 minutes
5 8 minutes
6 16 minutes
7 32 minutes
8 64 minutes

Après la huitième tentative, soit un peu plus de deux heures, la livraison passe au statut failed et n'est plus retentée. Un point de réception en échec n'est pas désactivé automatiquement : surveillez last_status.

Note

Les livraisons partent d'un traitement planifié chaque minute. Un événement arrive donc avec un délai de quelques secondes à une minute. Les livraisons d'un point de réception mis en pause restent en attente et partent dès sa réactivation.

Tester

curl -X POST https://api.novafisko.com/v1/firms/Fd7hQ2mN8sXa/webhooks/14/test \
  -H "Authorization: Bearer $NOVAFISKO_TOKEN"

L'événement webhook.test est envoyé immédiatement, sans nouvelle tentative en cas d'échec. La réponse est toujours 202 et contient la livraison avec le statut obtenu, delivered ou failed. La route est limitée à 12 tests par minute.

{
  "delivery": {
    "id": 90455,
    "event_id": "evt_01jb6zb1t4c8h2m6q0x3y7z9a5",
    "event": "webhook.test",
    "status": "delivered",
    "attempts": 1,
    "next_attempt_at": null,
    "response_status": 200,
    "response_excerpt": "ok",
    "duration_ms": 184,
    "delivered_at": "2026-10-05T09:40:12+00:00",
    "created_at": "2026-10-05T09:40:12+00:00",
    "payload": {"id": "evt_01jb6zb1t4c8h2m6q0x3y7z9a5", "event": "webhook.test", "api_version": "v1", "created_at": "2026-10-05T09:40:12Z", "firm": {"public_token": "Fd7hQ2mN8sXa", "name": "Fiduciaire Exemple"}, "company": null, "data": {"message": "Test event sent from NovaFisko.", "webhook_id": 14}}
  }
}

Journal des livraisons

curl "https://api.novafisko.com/v1/firms/Fd7hQ2mN8sXa/webhooks/14/deliveries?status=failed&per_page=25" \
  -H "Authorization: Bearer $NOVAFISKO_TOKEN"
Paramètre Description
status pending, delivered ou failed
event Nom d'un événement
per_page 25 par défaut, 100 au maximum
page Numéro de page
{
  "data": [
    {
      "id": 90412,
      "event_id": "evt_01jb6z3m8q2v7k4n5p9r0s1t2w",
      "event": "entry.posted",
      "status": "failed",
      "attempts": 8,
      "next_attempt_at": null,
      "response_status": 500,
      "response_excerpt": "Internal Server Error",
      "duration_ms": 412,
      "delivered_at": null,
      "created_at": "2026-10-05T09:23:20+00:00",
      "payload": {"id": "evt_01jb6z3m8q2v7k4n5p9r0s1t2w", "event": "entry.posted"}
    }
  ],
  "meta": {"current_page": 1, "last_page": 1, "per_page": 25, "total": 1}
}

response_excerpt contient les 2000 premiers caractères de la réponse de votre serveur, ou le message d'erreur de connexion. payload est le corps exact qui a été envoyé : vous pouvez le rejouer vous-même après avoir corrigé une panne. Les livraisons terminées (delivered ou failed) sont conservées 30 jours.

Bonnes pratiques

  • Vérifiez toujours la signature avant de traiter quoi que ce soit. Voir Vérifier les signatures.
  • Répondez vite. Accusez réception avec un 200, placez l'événement dans une file et traitez-le ensuite. Un traitement long provoque des dépassements de délai et des doublons.
  • Dédoublonnez sur id. Une même livraison peut vous parvenir plusieurs fois si votre réponse s'est perdue. Conservez les id d'événements déjà traités.
  • Ne supposez aucun ordre. Deux événements proches peuvent arriver dans le désordre, surtout après une nouvelle tentative. Appuyez-vous sur created_at et relisez la ressource en cas de doute.
  • Traitez l'événement comme un signal. Le contenu de data est un résumé. Pour l'état complet et à jour, appelez l'API.
  • Ignorez les événements inconnus. De nouveaux événements et de nouveaux champs peuvent apparaître sans changement de version. Répondez 200 à ce que vous ne connaissez pas.
  • Prévoyez les rafales. Un import en masse (extrait CODA, synchronisation Novadesko) produit une livraison par document ou par mouvement.
  • Combinez avec sync/pull. Utilisez le webhook comme déclencheur et le flux de changements comme source de vérité. Voir Synchroniser en continu.
Attention

Le cabinet de démonstration n'émet aucun webhook et la gestion des points de réception y est bloquée (403, code demo_mode).

Voir aussi