Chaque commande réussie exécutée par Billabex laisse une entrée dans le journal d’audit de l’organisation, quel qu’en soit l’auteur : une personne dans la webapp, un client tiers via cette API ou le serveur MCP, l’agent Billabex, ou un chemin système interne comme la synchronisation d’un connecteur.
L’historique d’un compte n’est pas une ressource à part. C’est ce même journal restreint à un compte, et c’est aussi la seule façon de lire l’historique d’un compte supprimé.
Endpoint
GET /api/public/v1/organizations/{organizationId}/audit
Exige organizations:read ou organizations:all.
| Paramètre | Type | Description |
|---|---|---|
accountId |
UUID | Ne garder que les entrées ayant agi sur ce compte |
actorTypes |
USER | AGENT | SYSTEM | SERVICE_ACCOUNT, répétable |
Ne garder que ces types d’acteur |
action |
chaîne | Ne garder que ce nom de commande, par exemple DeleteAccount |
search |
chaîne | Correspondance insensible à la casse dans les métadonnées, ou sur un identifiant de compte audité |
page |
entier | Page à renvoyer, à partir de 1, vaut 1 par défaut |
limit |
entier | Entrées par page, 20 par défaut, 100 au maximum |
Contrairement aux autres endpoints de liste, celui-ci pagine par numéro de page et non par curseur. Le journal est en ajout seul et se lit du plus récent au plus ancien : un numéro de page y est donc assez stable, et cela vous épargne un curseur pour une liste dont vous ne lisez la plupart du temps que la tête.
Réponse
{
"nodes": [
{
"id": "f5183985-1f0e-4c2b-9a35-1a2b3c4d5e6f",
"organizationId": "6effaee1-01d0-4205-b2da-911e28483dc6",
"accountIds": ["78ce0186-2b4f-4b1c-9c3a-0d1e2f3a4b5c"],
"actorType": "USER",
"actorId": "1b9d6bcd-bbfd-4b2d-9b5d-ab8dfbbd4bed",
"actorFullName": "Marie Dupont",
"actorEmail": "marie@example.com",
"actorDeleted": false,
"actorChannel": "WEBAPP",
"actorSource": null,
"action": "DeleteAccount",
"metadata": "{\"accountId\":\"78ce0186-2b4f-4b1c-9c3a-0d1e2f3a4b5c\"}",
"createdAt": "2026-08-27T19:40:35.542Z"
}
],
"totalCount": 1,
"page": 1,
"limit": 20,
"totalPages": 1
}
Lire une entrée
accountIdsliste tous les comptes sur lesquels la commande a agi. Une action en masse sur deux cents comptes produit une entrée portant deux cents identifiants, pas deux cents entrées. Une action à l’échelle de l’organisation, comme un changement de réglage, porte un tableau vide.actorTypedit qui :USERpour une personne,AGENTpour l’agent Billabex,SYSTEMpour un chemin interne.actorChanneln’est renseigné que pour une personne et dit par quelle porte elle est passée :WEBAPP,PUBLIC_APIouMCP.actorSourcen’est renseigné que pour un acteur système :CONNECTOR,SCHEDULER,WEBHOOK,ADMIN_API,MIGRATION,WORKFLOWouINTERNAL.actorDeletedne vauttrueque pour un utilisateur dont la suppression est connue. Un nom absent avecactorDeleted: falsesignifie que la projection n’est pas encore à jour, pas que la personne a disparu.metadataest l’entrée de la commande sérialisée, dont les valeurspassword,secretettokensont caviardées. Au-delà de quatre mille caractères, elle est remplacée par{"truncated": true, "action": "..."}.
Retrouver un compte qui n’existe plus
Une fois supprimé, un compte disparaît de toutes les listes, et son identifiant
seul ne vous apprend rien. Son nom, lui, survit dans les métadonnées des
commandes qui l’ont touché. Cherchez le nom, lisez l’identifiant sur l’entrée
CreateAccount, puis restreignez le journal à cet identifiant :
# 1. Retrouver le compte par son nom
curl -H "Authorization: Bearer $TOKEN" \
"$BASE_URL/api/public/v1/organizations/$ORG/audit?search=CCOG&action=CreateAccount"
# 2. Lire tout son historique, y compris qui l'a supprimé
curl -H "Authorization: Bearer $TOKEN" \
"$BASE_URL/api/public/v1/organizations/$ORG/audit?accountId=$ACCOUNT_ID"
search porte aussi sur les identifiants de comptes audités : coller un
identifiant retrouve donc toutes les commandes qui ont touché le compte, y
compris celles dont l’entrée ne le nommait pas.
MCP
Le même journal est accessible à un client MCP par list-audit-entries, avec les
mêmes filtres et le scope mcp:read.
Limites
- Le journal commence le 6 août 2026. Rien de plus ancien n’a été enregistré.
- L’écriture d’une entrée est au mieux : elle ne fait jamais échouer la commande qu’elle décrit. Une entrée manquante signifie donc « non attribué », jamais « n’a pas eu lieu ».
- Les commandes qui ne font qu’entretenir un état dérivé sont volontairement exclues, comme celles qui touchent aux mots de passe et aux jetons OAuth.