---
title: "Serveur MCP Billabex"
description: "Connecter un outil IA à Billabex par le Model Context Protocol (MCP), avec OAuth 2.1."
canonical: https://developer.billabex.com/fr/guides/mcp/
lang: fr
alternate: https://developer.billabex.com/en/guides/mcp/
last-updated: 2026-10-26
---

# Serveur MCP Billabex

> Connecter un outil IA à Billabex par le Model Context Protocol (MCP), avec OAuth 2.1.

Source: https://developer.billabex.com/fr/guides/mcp/
Langue: Français (fr)
Version anglaise: https://developer.billabex.com/en/guides/mcp/

Billabex expose un **serveur Model Context Protocol (MCP)**, pour qu'un outil IA accède aux données
Billabex avec OAuth 2.1. Ce guide décrit la découverte, l'autorisation, les outils et ressources
disponibles, et la façon de s'y connecter.

## L'endpoint MCP

Le serveur MCP utilise le transport **Streamable HTTP, sans état** :

`POST https://next.billabex.com/mcp`

## Le catalogue public des outils

`GET https://next.billabex.com/mcp/tools` renvoie tous les outils, prompts et ressources du serveur, chacun avec sa
description, son schéma JSON et le scope qu'il exige. Il n'exige **aucun identifiant** et est limité
par adresse IP sous la politique `public-api-anonymous`.

```bash
curl -s https://next.billabex.com/mcp/tools | jq '.tools[] | {name, scopes}'
```

Servez-vous-en pour décider s'il faut se connecter, pour générer des liaisons typées, ou pour
montrer à un utilisateur ce qu'autoriser Billabex lui donnerait, avant même de lancer un parcours
OAuth.

| Vue                      | Ce qu'elle contient                                                                                                    |
| ------------------------ | ---------------------------------------------------------------------------------------------------------------------- |
| par défaut (`view=full`) | Tout, schémas JSON d'entrée et de sortie compris. L'aperçu fidèle de ce que répond `tools/list`, environ 320 Ko.       |
| `?view=summary`          | Noms, descriptions, scopes et annotations, sans les schémas. Environ 40 Ko, de quoi décider si Billabex est pertinent. |

Une `view` que le serveur ne connaît pas est refusée par un `400` accompagné du corps d'erreur JSON
habituel, plutôt que remplacée en silence par une autre.

L'endpoint MCP, lui, reste protégé, et c'est délibéré. Un client MCP démarre le parcours OAuth parce
qu'il reçoit un `401` accompagné d'un défi `WWW-Authenticate` ; un serveur qui répondrait à
`initialize` sans jeton laisserait le client se croire connecté, et chaque appel d'outil échouerait
ensuite. Le catalogue donne à un agent ce dont il a légitimement besoin avant d'autoriser, sans
supprimer ce défi.

Rien dans ce catalogue n'est une donnée d'organisation : noms, descriptions et schémas sont le
contrat statique que ce guide documente déjà.

## Le parcours de découverte OAuth

Un client MCP suit la RFC 9728 (métadonnées de ressource protégée) et la RFC 8414 (métadonnées du
serveur d'autorisation OAuth) :

1. appeler `POST /mcp` sans jeton ;
2. le serveur répond avec un `WWW-Authenticate` portant `resource_metadata` et `scope` ;
3. récupérer l'URL des métadonnées de ressource pour découvrir les `authorization_servers` et les
   `scopes_supported` de `/mcp` ;
4. récupérer les métadonnées du serveur d'autorisation pour localiser les endpoints d'autorisation
   et d'enregistrement.

### Exemple de réponse 401

Le serveur MCP renvoie :

```
WWW-Authenticate: Bearer resource_metadata="https://next.billabex.com/.well-known/oauth-protected-resource", scope="mcp:read mcp:write"
```

### Métadonnées de ressource protégée, par chemin

Pour `/mcp`, demandez :

`https://next.billabex.com/.well-known/oauth-protected-resource/mcp`

La réponse contient :

```json
{
  "resource": "https://next.billabex.com",
  "authorization_servers": ["https://next.billabex.com"],
  "scopes_supported": ["mcp:read", "mcp:write"],
  "bearer_methods_supported": ["header"]
}
```

### Métadonnées du serveur d'autorisation

`GET https://next.billabex.com/.well-known/oauth-authorization-server`

Renvoie l'`authorization_endpoint`, le `token_endpoint` et le `registration_endpoint` nécessaires à
OAuth 2.1 avec PKCE.

## Scopes

MCP utilise deux scopes :

| Scope       | Accès    | Description                                              |
| ----------- | -------- | -------------------------------------------------------- |
| `mcp:read`  | Lecture  | Lister et consulter factures, comptes et communications. |
| `mcp:write` | Écriture | Créer, modifier et supprimer.                            |

## Consentement

Si une requête d'autorisation omet le paramètre `scope`, Billabex applique par défaut
`mcp:read mcp:write` aux clients MCP.

## Détails du protocole

- **Transport** : Streamable HTTP, en mode sans état.
- **Authentification** : en-tête `Authorization: Bearer <jeton>`, obligatoire.
- **Limite de débit** : 300 requêtes par minute et par jeton, en fenêtre glissante.
- **En-tête CORS** : un client peut envoyer l'en-tête `Mcp-Protocol-Version`.

## Ressources

Les ressources MCP fournissent des métadonnées qu'un outil IA peut lire pour comprendre ce que
Billabex sait faire.

| URI                   | Description                                                          |
| --------------------- | -------------------------------------------------------------------- |
| `billabex://about`    | Présentation de la plateforme, proposition de valeur, fonctions clés |
| `billabex://concepts` | Entités, statuts, relations et règles métier                         |
| `billabex://workflow` | Le parcours de suivi des paiements pas à pas, tâches comprises       |

Elles aident un agent IA à comprendre :

- **About** : ce qu'est Billabex, et comment il aide une entreprise à encaisser ses créances ;
- **Concepts** : le modèle de domaine, factures, comptes, soldes clients, communications et tâches ;
- **Workflow** : le parcours standard, de la facture échue jusqu'à l'encaissement.

## Prompts

Les prompts MCP sont des modèles orientés métier, pour les tâches courantes d'un agent IA.
Contrairement aux outils, ils utilisent des **paramètres lisibles par un humain** (nom de compte,
nom d'organisation) plutôt que des identifiants techniques.

| Prompt                       | Paramètres                                           | Cas d'usage                                                                |
| ---------------------------- | ---------------------------------------------------- | -------------------------------------------------------------------------- |
| `financial-snapshot`         | `organizationName?`, `period?` (7/30/tous les jours) | Vue des encours, principaux comptes, trésorerie                            |
| `account-360`                | `accountName`, `organizationName?`                   | Synthèse complète d'un compte : factures, communications, tâches           |
| `daily-plan`                 | `organizationName?`                                  | Plan d'action du jour : tâches, rappels, suivis                            |
| `daily-digest`               | `organizationName?`                                  | Résumé de l'activité et des évolutions du jour                             |
| `communication-intelligence` | `accountName?`, `organizationName?`                  | Réponses récentes, promesses de paiement, comptes ayant répondu sans payer |
| `risk-report`                | `organizationName?`                                  | Comptes à risque, comptes silencieux, comptes sans contact                 |
| `silent-accounts`            | `organizationName?`, `period?` (30/90/tous)          | Comptes n'ayant jamais répondu à une relance par email                     |
| `blocked-accounts`           | `organizationName?`                                  | Comptes dont les emails rebondissent ou sont signalés comme indésirables   |
| `inbound-email-digest`       | `organizationName?`, `period?` (7/30/90 jours)       | Synthèse des emails entrants sur une période récente                       |

### Comment fonctionne un prompt

1. **L'utilisateur donne des noms** : vous appelez le prompt avec un nom de compte comme « Acme Corp » plutôt qu'un UUID
2. **L'agent résout les identifiants** : le prompt lui demande d'utiliser `list-accounts` et `list-organizations` pour trouver les identifiants correspondants
3. **L'agent collecte les données** : il appelle plusieurs outils pour rassembler ce qui est pertinent
4. **L'agent présente le résultat** : formaté naturellement, sans exposer d'identifiant technique

### Exemple : financial-snapshot

```json
{
  "name": "financial-snapshot",
  "arguments": {
    "organizationName": "My Company",
    "period": "30days"
  }
}
```

L'agent va :

1. utiliser `list-organizations` pour trouver « My Company » ;
2. utiliser `list-customer-balances` pour obtenir les encours ;
3. utiliser `list-invoices` pour trouver les factures à échéance proche ;
4. présenter une vue d'ensemble formatée de votre situation financière.

## Référence des outils

Les outils sont groupés par domaine. Les outils de **lecture** exigent `mcp:read`, ceux d'**écriture** `mcp:write`.

Les sorties portent des champs lisibles par un humain, comme `displayName` et `summary` quand ils
existent. Préférez-les pour vos réponses à l'utilisateur, et gardez les identifiants pour les appels
d'outils suivants.

Tous les outils portent des annotations (`readOnlyHint`, `destructiveHint`, `idempotentHint`,
`openWorldHint`) qui aident l'agent à comprendre leur comportement, ainsi qu'un `title` lisible que
les clients affichent à la place du nom de l'outil.

### Facturation

| Outil                                | Accès    | Description                                                                                                   |
| ------------------------------------ | -------- | ------------------------------------------------------------------------------------------------------------- |
| `list-invoices`                      | Lecture  | Lister les factures avec le mode de paiement du compte (paginé).                                              |
| `export-invoices`                    | Lecture  | Exporter toutes les factures filtrées, ou les avoirs avec `documentType: "creditNotes"`, en `.xlsx` : renvoie un lien de téléchargement valable 15 minutes. |
| `get-aging-balance`                  | Lecture  | Totaux nets de la balance âgée par devise.                                                                    |
| `list-aging-balance-by-account`      | Lecture  | Ventilation nette et paginée par compte et par devise.                                                        |
| `get-invoice`                        | Lecture  | Consulter une facture par identifiant, ou par numéro dans un compte ou une organisation.                      |
| `create-invoice`                     | Écriture | Créer une facture avec un fichier en base64 et un `sourceId` facultatif.                                      |
| `update-invoice`                     | Écriture | Corriger un seul aspect par appel : le montant total payé (`paidAmount`), la date d'échéance ou l'échéancier. |
| `set-invoice-dunning-block`          | Écriture | Exclure une facture des relances, ou l'y réintégrer ; elle reste due, le compte continue.                     |
| `delete-invoice`                     | Écriture | Supprimer une facture.                                                                                        |
| `list-accounts`                      | Lecture  | Lister les comptes avec leur type, mode de paiement et identifiant source.                                    |
| `get-account`                        | Lecture  | Consulter le type, les détails et les contacts d'un compte.                                                   |
| `get-account-financial-overview`     | Lecture  | Expliquer le solde d'un compte et lister chaque échéance de facture.                                          |
| `create-account`                     | Écriture | Créer un compte, avec type et mode de paiement facultatifs.                                                   |
| `update-account`                     | Écriture | Modifier type, nom, devise, adresse ou mode de paiement d'un compte.                                          |
| `delete-account`                     | Écriture | Supprimer un compte client.                                                                                   |
| `list-contacts`                      | Lecture  | Lister les contacts d'un compte.                                                                              |
| `get-contact`                        | Lecture  | Consulter un contact précis.                                                                                  |
| `create-contact`                     | Écriture | Ajouter un contact à un compte.                                                                               |
| `delete-contact`                     | Écriture | Retirer un contact.                                                                                           |
| `upsert-contact`                     | Écriture | Créer ou mettre à jour par correspondance email ou nom, modifier par `contactId`, ou activer/désactiver.      |
| `list-credit-notes`                  | Lecture  | Lister les avoirs avec le mode de paiement du compte (paginé).                                                |
| `get-credit-note`                    | Lecture  | Consulter un avoir, par identifiant ou par numéro.                                                            |
| `create-credit-note`                 | Écriture | Créer un avoir, avec un `sourceId` facultatif.                                                                |
| `update-credit-note-refunded-amount` | Écriture | Enregistrer un remboursement partiel ou total.                                                                |
| `delete-credit-note`                 | Écriture | Supprimer un avoir.                                                                                           |
| `apply-credit-allocation`            | Écriture | Imputer un avoir sur une facture.                                                                             |
| `remove-credit-allocation`           | Écriture | Retirer une imputation d'avoir.                                                                               |
| `add-account-tags`                   | Écriture | Attacher des étiquettes existantes à un compte.                                                               |
| `remove-account-tag`                 | Écriture | Détacher une étiquette d'un compte.                                                                           |
| `link-account-source`                | Écriture | Relier une source externe à un compte.                                                                        |
| `update-account-source`              | Écriture | Mettre à jour la date de dernière synchronisation d'une source.                                               |
| `unlink-account-source`              | Écriture | Délier une source externe.                                                                                    |
| `check-account-bodacc`               | Écriture | Vérifier les procédures collectives et proposer des SIREN candidats.                                          |
| `get-account-portal-link`            | Lecture  | Lire le lien du portail client d'un compte.                                                                   |
| `ensure-account-portal-link`         | Écriture | Obtenir ou créer ce lien, sans jamais remplacer un lien existant.                                             |
| `regenerate-account-portal-link`     | Écriture | Remplacer ce lien ; toutes les copies précédentes cessent de fonctionner.                                     |

`update-invoice` reçoit l'UUID d'une facture, exactement un champ parmi `paidAmount`, `dueDate` et
`installments`, et un `skipConfirmation` facultatif. Chacun est une correction distincte : un appel
qui n'en porte aucun, ou plusieurs, est refusé sans rien écrire, si bien qu'un appel ne s'applique
jamais à moitié. La réponse est l'accusé de réception de la facture, `paymentSchedule` compris.
L'outil est annoté comme destructif : le client demande à l'utilisateur d'approuver l'appel.
`skipConfirmation` reste accepté et ne change rien.

- `paidAmount` est le nouveau total payé sur la facture, jamais un montant à ajouter.
  Accompagnez-le de `expectedPaidAmount`, le montant payé que vous avez lu (accepté avec
  `paidAmount` seulement) : si une autre mise à jour est passée entre-temps, l'appel est refusé avec
  `app-invoicing.invoice.paid-amount-conflict`, qui porte le montant actuel.
- `dueDate` est une date calendaire qui ne peut pas précéder la date d'émission. Réécrire la même
  date ne change rien : un script de correction après import peut donc être rejoué sans risque.
  Déplacer l'échéance recalcule le retard et replanifie les relances à venir ; une relance déjà
  modifiée à la main reste intacte. Une facture payée en plusieurs échéances la refuse : corrigez
  plutôt ses `installments`.
- `installments` est l'échéancier complet, chaque échéance portant une date calendaire et un montant
  positif. Envoyez un tableau vide pour retirer l'échéancier.

`get-account-financial-overview` rend `referenceDate` et le `dueDate` de chaque échéance sous la même
forme que l'API publique : un objet `{ "year": 2026, "month": 10, "day": 15 }`, date calendaire sans
heure ni fuseau. Lisez chaque composante par son nom, l'ordre des propriétés n'étant pas garanti, et
affichez la date depuis ses composantes, sans la convertir en instant UTC. Les instants, comme
`sentAt`, restent des chaînes ISO 8601. Voir [Dates et instants](/fr/guides/demarrage/#dates-et-instants).

`upsert-contact` couvre toutes les écritures sur un contact existant. Sans `contactId`, il cherche
une correspondance sur l'email ou le nom, puis met à jour le contact trouvé ou en crée un. Avec
`contactId`, il modifie ce contact, et `null` efface `fullName` (le contact doit garder une adresse
email), `email` ou `jobTitle`. Avec `contactId` et `enabled`, sans aucun autre champ, il active ou
désactive le contact ; `enabled` est refusé dans toute autre combinaison : modifiez un contact et son
statut en deux appels. Un contact désactivé n'est jamais sollicité spontanément par l'agent. Le
réactiver est une décision humaine : `enabled: true` ne devrait donc pas servir à annuler une
désactivation que le client a demandée.

`create-contact` et `upsert-contact` prennent une liste `phones` d'objets
`{ number, countryCode? }`. Un numéro commençant par `+` ou `00` porte son propre indicatif ; tout
autre numéro est national et **exige** `countryCode` (ISO 3166-1 alpha-2), Billabex ne devinant
jamais un pays. Les contacts sont renvoyés avec `phones: [{ number, type, country, origin }]`, où
`type` (`Mobile`, `Landline` ou `Unknown`) et `country` sont déduits du numéro lui-même et en
lecture seule. Écrire `phones` ne remplace que ce qu'une personne a saisi, jamais les numéros
apportés par un connecteur (`origin: "Connector"`) ; omettez le champ pour ne rien toucher, envoyez
`[]` pour effacer les numéros manuels. Seul un mobile de France (`FR`) ou de Guyane (`GF`) peut
recevoir un SMS. Voyez [Contacts](/fr/guides/contacts/) pour le modèle complet.

Les documents créés par `create-invoice` et `create-credit-note` portent le canal `Mcp`, distinct du
`PublicApi` que pose l'API REST. Ils peuvent vivre dans une organisation par ailleurs synchronisée
avec un ou plusieurs connecteurs : voyez [Sources de compte](/fr/guides/sources-de-compte/) pour
comprendre pourquoi la `source` d'un compte ne dit jamais d'où vient l'un de ses documents.

Les deux outils de création prennent aussi un `sourceId` facultatif, votre propre identifiant pour
le document. Il est figé à la création, renvoyé sur les factures et les avoirs aux côtés de
`connectionId`, unique dans l'organisation (une recréation avec la même valeur est refusée plutôt
que dupliquée), et filtrable sur `list-invoices` et `list-credit-notes` avec `is`, `isAnyOf`,
`isEmpty` et `isNotEmpty` uniquement, parce qu'il est sensible à la casse. Les numéros de documents,
eux, sont uniques **par compte** : `get-invoice` et `get-credit-note` répondent exactement quand on
leur donne `accountId` plus `number`, tandis qu'une recherche par `organizationId` plus `number`
peut répondre n'importe lequel de plusieurs homonymes.

Pour récupérer un ensemble connu de documents en un seul appel plutôt qu'un appel par document,
filtrez `number` avec `isAnyOf` sur `list-invoices` ou `list-credit-notes` :
`{"field": "number", "operator": "isAnyOf", "values": ["FA-2026/1", "FA-2026/2"]}`, 50 numéros au
maximum par condition. Les numéros se comparent sur leur forme canonique, donc la ponctuation, les
espaces et la casse n'ont pas besoin d'être exacts ; une valeur à laquelle il ne reste rien après
cela est écartée plutôt que de vider toute la condition. Comme un numéro est unique par compte et
non par organisation, ajoutez un `accountId` quand des homonymes rendraient la réponse ambiguë.

`create-contact` et `upsert-contact` peuvent renvoyer un tableau `warnings` à côté
du contact quand une partie de l'écriture n'a pas été retenue : un `fullName` ou une `language`
qu'une personne possède et qu'une synchronisation automatisée a tenté d'écraser (`MANUALLY_SET`), ou
un numéro qu'une synchronisation a envoyé sans qu'il puisse être normalisé (`UNPARSEABLE`). Le reste
de l'écriture a bien été appliqué. Lisez le tableau plutôt que de vous fier à un succès nu.

`list-invoices` et `list-credit-notes` renvoient `accountPaymentMethod` valant `DIRECT_DEBIT`,
`BANK_TRANSFER` ou `null`. La valeur est lue sur le compte au moment de la requête : modifier le
compte ne demande donc pas de mettre à jour chaque document.

Pour un compte relié à un connecteur, `list-accounts` renvoie la référence client du connecteur dans
`source.sourceId`. Le même champ reste disponible sur `lastUnlinkedSource` après un détachement.
Aucun appel à `get-account` n'est nécessaire pour rapprocher un compte listé du client externe.

`list-accounts`, `list-credit-notes`, `get-account`, `get-invoice` et `get-credit-note` renvoient
`readOnlyReason` : `null` quand l'enregistrement accepte les écritures, sinon le connecteur qui les
écraserait. Lisez-le plutôt que de déduire un verdict de `channel` ou de `source`. `list-invoices`
ne le porte pas, sa projection ne pouvant pas distinguer une facture historique ; appelez
`get-invoice` quand vous avez besoin du verdict pour l'une de ces lignes.

Les outils de compte exposent `accountType` valant `Professional`, `Consumer` ou `Unknown`.
`create-account` et `update-account` acceptent le même champ. L'omettre à la création fait déduire
`Professional` d'un identifiant légal ; sinon le compte reste `Unknown`. Un connecteur standard
contrôle la valeur et ne peut pas être surchargé par MCP ; une source `Custom` reste modifiable. Les
comptes `Consumer` ne sont pas vérifiés au BODACC, tandis que leur SIREN et leur numéro de TVA, s'ils
existent, restent accessibles par l'API.

Les deux outils acceptent aussi `paymentMethod` valant `DIRECT_DEBIT`, `BANK_TRANSFER` ou `null`.
L'omettre ou envoyer `null` à la création laisse le mode de paiement inconnu.

Un compte sous procédure collective porte aussi ce dont un créancier a besoin pour agir :
`bodaccNoticeDate` (date de publication de l'annonce, point de départ du délai de deux mois pour
déclarer sa créance), `bodaccJudgmentDate` (le jugement lui-même, généralement quelques jours plus
tôt), `bodaccPractitioner` et `bodaccNoticeUrl`. Pour tout délai, lisez `bodaccNoticeDate` : c'est
la date de publication qui compte, pas celle du jugement. Contrairement aux autres dates calendaires,
ces deux champs sont des jours envoyés en instants à minuit UTC (`"2026-08-23T00:00:00.000Z"`) :
lisez leur partie date en UTC, sans jamais les convertir en heure locale.

`bodaccPractitioner` est le mandataire auprès duquel déclarer la créance, sous forme d'une ligne de
texte libre citée de l'annonce : un cabinet, souvent une personne, et une adresse postale. Le BODACC
ne porte aucun champ structuré pour cela et ne publie jamais d'email ni de téléphone : rien de plus
précis ne peut donc être renvoyé, et le champ est nul quand l'annonce formule la désignation d'une
façon qui ne peut pas être analysée. `bodaccNoticeUrl` pointe l'annonce elle-même et sert de repli
dans ce cas. Les deux sont nuls tant qu'aucune procédure n'est ouverte, et pour une société radiée
après une liquidation clôturée pour insuffisance d'actif, où plus aucune créance ne peut être
déclarée.

`check-account-bodacc` exige `mcp:write` et prend un `accountId` plus un `siren` facultatif (SIREN à
9 chiffres ou SIRET à 14). Sans identifiant confirmé, il renvoie les candidats proposés sans en
adopter aucun : une recherche élargie n'établit jamais une identité à elle seule. Confirmez
l'identifiant sur un document qui nomme le débiteur, ou sur sa fiche client, avant de l'envoyer.

L'équivalent REST est `POST /api/public/v1/accounts/{accountId}/bodacc-check`, avec le scope
`accounts:all` et un corps valant `{}` ou `{"siren":"482309382"}`. La vérification est aussi
disponible pour les comptes gérés par connecteur.

La réponse porte `siren`, `status`, `checkedAt`, `companyName`, `noticeUrl` et `candidates`, une
liste d'objets `{siren,name,address}`. Le statut est nul quand le compte sort du périmètre des
sociétés françaises. L'appel peut enregistrer une vérification, suspendre les relances et ouvrir une
tâche : ce n'est pas une lecture. Une suspension légale ou manuelle reste en place, même quand
l'identification change.

Quand une procédure a été signalée mais que le SIREN reste inconnu, une tâche le demande. Elle se lit
et se traite par les outils de tâches habituels. Clore cette tâche ne renseigne pas le SIREN et ne
lève pas la suspension légale.

### Suivi des paiements

| Outil                                   | Accès    | Description                                                                          |
| --------------------------------------- | -------- | ------------------------------------------------------------------------------------ |
| `list-customer-balances`                | Lecture  | Lister les encours clients (paginé).                                                 |
| `get-customer-balance`                  | Lecture  | Détail d'un solde, avec ses factures et ses avoirs.                                  |
| `list-communications`                   | Lecture  | Lister tous les canaux d'une organisation ou d'un compte, filtrables côté serveur.   |
| `get-communication`                     | Lecture  | Lire une communication à partir de l'id et du type d'une ligne de la liste.          |
| `list-silent-accounts`                  | Lecture  | Lister les comptes relancés au moins une fois et qui n'ont jamais répondu.           |
| `update-outgoing-communication`         | Écriture | Modifier un email sortant planifié.                                                  |
| `send-outgoing-communication`           | Écriture | Envoyer un email immédiatement au nom de l'agent, à des destinataires libres.        |
| `preview-outgoing-message`              | Lecture  | Prévisualiser et chiffrer un SMS ou un courrier ; `includePdf` ajoute le PDF rendu.  |
| `send-outgoing-message`                 | Écriture | Envoyer un SMS ou un courrier payant, avec une clé d'idempotence obligatoire.        |
| `refresh-outgoing-message-tracking`     | Écriture | Actualiser le suivi postal et l'accusé d'un courrier, sans envoi ni crédit.          |
| `get-account-dunning-status`            | Lecture  | Savoir si un compte est relancé, sinon pourquoi, et lire sa promesse de paiement.    |
| `pause-account-dunning`                 | Écriture | Suspendre les relances avec un motif factuel, ou jusqu'à une date.                   |
| `resume-account-dunning`                | Écriture | Lever les suspensions levables par un humain, et renvoyer les protections restantes. |
| `withdraw-account-payment-date-request` | Écriture | Retirer la consigne de demander au débiteur une date de paiement.                    |

`get-account-dunning-status` répond à la question « pourquoi rien ne part sur ce compte ». Son
`silenceReason` nomme l'unique cause, classée une seule fois côté serveur, de sorte que la réponse
soit la même que celle de la webapp et que celle de `GET /accounts/{accountId}/dunning` sur l'API
REST : `suspended`, puis `blocking-task`, puis `payment-decision-overdue`, puis
`payment-declaration`, puis `all-invoices-blocked`, puis `below-minimum-amount`, puis
`connector-sync-stale`, puis `first-reminder-delay`, et `null` quand rien ne retient le compte.
Lisez-le plutôt que de classer vous-même les indicateurs individuels. `payment-declaration` désigne
un paiement annoncé encore dans son délai de grâce ; `payment-decision-overdue`, un paiement annoncé
dont le délai est écoulé et qu'aucune tâche ouverte ne demande plus de trancher. Celui-ci ne se lève
jamais seul : enregistrez une décision avec `resolve-account-payment-declaration`.

`set-invoice-dunning-block` avec `blocked: true` exclut une seule facture des relances automatiques
pendant que les autres factures du compte continuent d'être relancées : la facture reste due et dans
le solde, mais aucune relance ne la réclame, ne la cite ni ne la joint, et elle ne pilote plus la
cadence. À utiliser pour un litige, une livraison partielle ou un avoir attendu ; une `note` interne
facultative (500 caractères au plus) n'est jamais envoyée au débiteur. Ce n'est ni
`pause-account-dunning`, qui arrête tout le compte, ni `record-account-payment-declaration`, qui
enregistre un fait de paiement. Quand toutes les factures ouvertes d'un compte sont exclues,
`get-account-dunning-status` répond `all-invoices-blocked`. `blocked: false` lève l'exclusion et
refuse une `note` ; `get-invoice` et `list-invoices` la rendent sous `dunningBlock` (`note`,
`blockedAt`, `blockedBy`), `null` quand la facture est relancée comme les autres.

La même réponse renvoie `collectionStatus` : `Open`, `Settled`, `WrittenOff` ou `NoInvoice`.
`resume-account-dunning` accepte un `reason` facultatif ; passez `written-off` pour rouvrir un compte
passé en perte sans lever aucune autre suspension. `list-accounts` peut filtrer sur
`collectionStatus`, même si ce champ dérivé n'est pas ajouté à chaque élément de la liste.

Le statut porte aussi `paymentDateRequest` : la consigne, donnée par une personne, de demander au
débiteur une date de paiement réaliste plutôt que le paiement (`taskId`, `instructedAt`), ou `null`.
Les relances ne demandent une date que tant que le compte est dû et qu'aucune promesse de paiement
active, aucun prélèvement ni aucun échéancier ne s'applique. `withdraw-account-payment-date-request`
retire la consigne : les relances suivantes demandent de nouveau le paiement. L'appel est
idempotent. L'équivalent REST est
`POST /api/public/v1/accounts/{accountId}/dunning/payment-date-request/withdraw`.

`list-communications` lit une projection déjà jointe, et filtre, trie et pagine côté serveur.
Il couvre tous les canaux (`IncomingEmail`, `OutgoingEmail`, `OutgoingSms`, `OutgoingTrackedLetter`,
`OutgoingRegisteredLetter`) ; passez `accountId` pour le restreindre à un compte. Chaque ligne porte
un `id` et un `type` : passez les deux à `get-communication` pour lire le contenu. Seule la section
correspondant au `type` (`incomingEmail`, `outgoingEmail` ou `message`) est renseignée ; le
`message.pdfUrl` d'un courrier est un lien signé de courte durée, renouvelé en relisant. Au-delà de
ses propres arguments `accountId`, `type` et de date, il prend les conditions `filters` décrites plus bas dans
[Filtrer les listes](#filtrer-les-listes) : `direction`, `status`, `deliveryStatus`, `accountName`,
`contactName`, `subject` et `date`.

Une communication survit au compte auquel elle appartenait. Quand ce compte a été supprimé,
l'élément porte encore son `accountId` et son `accountFullName`, et `accountDeletedAt` donne
l'instant de la suppression. Traitez un `accountDeletedAt` non nul comme « ne pas tenter de lire ce
compte » : les outils de compte répondent comme s'il n'avait jamais existé. Il vaut null sur tout
compte encore présent.

### Email

| Outil         | Accès   | Description                                                                         |
| ------------- | ------- | ----------------------------------------------------------------------------------- |
| `list-emails` | Lecture | Lister la boîte mail (entrants et sortants), ou un seul sens avec `type`.           |
| `get-email`   | Lecture | Lire un email, avec son corps simplifié et des liens de pièces jointes temporaires. |

### Plateforme

| Outil                       | Accès    | Description                                                         |
| --------------------------- | -------- | ------------------------------------------------------------------- |
| `list-organizations`        | Lecture  | Lister les organisations de l'utilisateur, avec leurs crédits.      |
| `get-organization`          | Lecture  | Détail d'une organisation, solde de crédits compris.                |
| `update-organization`       | Écriture | Modifier le nom et le domaine email, ou les réglages.               |
| `list-organization-members` | Lecture  | Lister les membres et les métadonnées de leur photo de profil.      |
| `list-tags`                 | Lecture  | Lister les étiquettes de l'organisation et les étiquettes standard. |
| `create-tag`                | Écriture | Créer une étiquette appartenant à l'organisation.                   |

`update-organization` écrit soit l'identité (`name`, `emailDomainName`), soit les réglages décrits
plus bas, jamais les deux : ce sont deux écritures distinctes, donc un appel qui porte les deux est
refusé. Seuls les champs envoyés sont appliqués. L'outil répond avec l'identifiant, l'identité et les
réglages de l'organisation ; lisez le reste avec `get-organization`.

`list-organizations` et `get-organization` renvoient `credits`, le solde prépayé qui paie les
relances par SMS et par courrier. Voyez [Crédits](#crédits) plus bas.

Ils renvoient aussi `firstReminderDelay`, le défaut de première relance, `{ days, direction }` ou
`null`. Il s'applique à tout mode sans règle propre et aux comptes dont le mode de paiement est
inconnu, et c'est la seule façon de poser un défaut **avant** l'échéance : `firstReminderDelayDays`
ne dit que « N jours après ». `update-organization` l'écrit sous le même nom, avec `days`
d'au moins 1, ou `null` pour revenir à la cadence standard (un rappel 3 jours ouvrés avant
l'échéance). Ne l'envoyez pas avec `firstReminderDelayDays`.

Ils renvoient aussi les deux règles effectives de première relance,
`firstReminderDelayForDirectDebit` et `firstReminderDelayForBankTransfer`, chacune
`{ days, direction }` avec `direction` valant `BEFORE` ou `AFTER` l'échéance, ou `null` quand ni
surcharge ni `firstReminderDelayDays` n'est posé, ou quand le mode a été désactivé.
`firstReminderDelayForDirectDebitFollowsDefault` et `firstReminderDelayForBankTransferFollowsDefault`
indiquent si chaque mode suit encore `firstReminderDelayDays` ou porte sa propre règle.
`update-organization` écrit les règles sous les mêmes noms : une clé absente laisse ce mode
tel quel, un `null` explicite désactive la règle de ce mode quel que soit `firstReminderDelayDays`.
Pour qu'un mode suive de nouveau `firstReminderDelayDays`, envoyez son champ `...FollowsDefault` à
`true`, sans règle pour ce même mode. Un compte dont le mode de paiement est inconnu suit
`firstReminderDelayDays` seul, jamais l'une de ces deux règles.

Les emails générés regroupent les montants par facture strictement inférieurs au seuil
converti dans la devise du compte. Le total et les pièces justificatives restent inchangés.
Lorsque le total du groupe reste sous le seuil, il ne durcit pas le ton de la relance.
Cette règle utilise le même réglage depuis la webapp, l'API publique et le MCP.

Ils renvoient aussi le seuil de relance sous forme de paire, `minimumDunningAmount` et
`minimumDunningAmountCurrency` : un compte est encore relancé quand son solde égale le montant, et
ne l'est plus quand il est strictement en dessous, le solde étant d'abord converti dans la devise du
seuil. Une nouvelle organisation démarre à `2 EUR`. Une devise `null` à côté d'un montant non nul
est un réglage historique, écrit avant l'existence des devises : le montant est alors comparé dans
la devise propre à chaque compte.

`update-organization` écrit la paire :

```json
{ "minimumDunningAmount": 5, "minimumDunningAmountCurrency": "USD" }
```

- les deux clés ensemble posent la paire ;
- le montant seul conserve la devise déjà posée, ou conserve la comparaison historique s'il n'y en
  a pas ;
- `{ "minimumDunningAmount": null, "minimumDunningAmountCurrency": null }` retire le seuil ;
- une devise sans son montant est refusée, comme un montant non nul avec une devise explicitement
  `null`.

Ils renvoient aussi `logo`, l'image téléversée pour l'organisation, ou `null`. `null` ne signifie pas
qu'aucun logo n'est affiché : sans image téléversée, un logo est dérivé du domaine email de
l'organisation à l'affichage, et rien n'est stocké. Le logo se pose depuis les paramètres de
l'organisation, dans la webapp.

### Tâches

| Outil                                     | Accès    | Description                                                                                                           |
| ----------------------------------------- | -------- | --------------------------------------------------------------------------------------------------------------------- |
| `list-account-tasks`                      | Lecture  | Rechercher les tâches de l'organisation (paginé).                                                                     |
| `get-account-task`                        | Lecture  | Détail d'une tâche et format de réponse attendu.                                                                      |
| `cancel-account-task`                     | Écriture | Annuler une tâche, avec un `reason` facultatif. Refusé sur une vérification de paiement.                              |
| `close-account-task`                      | Écriture | Terminer une tâche traitée ailleurs, sans modèle ni email. Seulement une `NeedUserInput` qui demande une information. |
| `set-pending-account-task`                | Écriture | Mettre une tâche de côté, avec une date `pendingUntil` facultative. Sans elle, elle revient au bout de 30 jours.      |
| `add-account-task-text-interaction`       | Écriture | Ajouter une réponse texte à une tâche.                                                                                |
| `decide-account-task-eligibility`         | Écriture | Décider de l’éligibilité du contact.                                                                                  |
| `decide-account-task-contact-change`      | Écriture | Approuver ou refuser les changements de contacts proposés.                                                            |
| `decide-account-task-first-outreach`      | Écriture | Autoriser ou refuser la première relance.                                                                             |
| `provide-account-task-contact`            | Écriture | Fournir le contact demandé.                                                                                           |
| `decide-account-task-next-action`         | Écriture | Choisir la prochaine action.                                                                                          |
| `decide-account-task-payment-arrangement` | Écriture | Accorder, refuser ou conserver un échéancier.                                                                         |
| `decide-account-task-email-draft`         | Écriture | Envoyer ou abandonner le brouillon en attente.                                                                        |
| `confirm-account-task-recipients`         | Écriture | Confirmer les destinataires.                                                                                          |
| `confirm-account-task-resume`             | Écriture | Confirmer la reprise ou le maintien en pause.                                                                         |
| `acknowledge-unknown-account-task`        | Écriture | Accuser réception d’une tâche Unknown.                                                                                |

Une tâche `NeedUserInput` peut exposer `extraData.pendingCommunicationId`. Lisez alors le brouillon
avec `get-communication` (`type: "OutgoingEmail"`), modifiez-le au besoin avec `update-outgoing-communication`, puis
appelez `decide-account-task-email-draft` avec `accountTaskId` et
`value: { "communicationId": "…", "decision": "send" }`. La décision `discard` l'abandonne.
Une réponse texte ne l'approuve jamais.

**Le contrat de `list-account-tasks` a changé.** Il exigeait autrefois un `accountId`, renvoyait
d'un coup toutes les tâches actives de ce compte et ne paginait jamais. Il prend désormais
`organizationId`, `accountId` devenant facultatif, renvoie les tâches de tout statut par défaut, et
pagine. Son ancien argument `type` est devenu une condition de filtre `type`. Les éléments gardent
leur forme complète, fil d'interactions `thread` compris : la projection sélectionne la page, puis
chaque ligne est réhydratée depuis son agrégat.

## Filtrer les listes

`list-accounts`, `list-invoices`, `list-credit-notes`, `list-communications` et
`list-account-tasks` prennent un argument `filters` : un tableau de conditions, chacune
`{ field, operator, value }` ou `{ field, operator, values }`.

```json
{
  "organizationId": "123e4567-e89b-12d3-a456-426614174000",
  "filters": [
    {
      "field": "status",
      "operator": "isAnyOf",
      "values": ["Overdue", "Issued"]
    },
    { "field": "dueDate", "operator": "before", "value": "2026-01-01" },
    { "field": "remainingBalance", "operator": "greaterThan", "value": "1000" }
  ],
  "first": 50
}
```

- Les conditions se combinent en `ET` ; les valeurs d'une même condition en `OU`.
- Un champ ne porte qu'une condition, et il y a au plus 20 conditions par appel.
- Les champs et les opérateurs viennent d'une liste blanche par outil, donnée dans la description de
  chacun. Un champ inconnu, ou un opérateur que le champ n'accepte pas, est refusé, jamais ignoré.
- Les dates s'écrivent `AAAA-MM-JJ` et se comparent par jour entier. La comparaison de texte ignore
  la casse et les accents.

| Outil                           | Champs                                                                                                                                                                                                                            |
| ------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `list-accounts`                 | `accountName`, `tags`, `dunningPaused`, `collectionStatus`, `followUpStatus`, `daysOverdue`, `overdueInvoiceCount`, `overdueBalance`, `remainingBalance`, `hasPaymentSchedule`, `paymentMethod`, `collectiveProceeding`, `source` |
| `list-aging-balance-by-account` | `accountName`, `tags`, `dunningPaused`, `paymentMethod`, `collectiveProceeding`, `source`, `notDue`, `d0_30`, `d31_60`, `d61_90`, `d90Plus`, `totalOutstanding`, `overdueOutstanding`, `overdueRate`                              |
| `list-invoices`                 | `number`, `accountName`, `tags`, `status`, `dueDate`, `paymentSchedule`, `paymentMethod`, `totalAmount`, `remainingBalance`, `channel`                                                                                            |
| `list-credit-notes`             | `number`, `accountName`, `tags`, `status`, `issuedDate`, `paymentMethod`, `totalAmount`, `remainingAmount`, `channel`                                                                                                             |
| `list-communications`           | `direction`, `status`, `deliveryStatus`, `accountName`, `contactName`, `subject`, `date`                                                                                                                                          |
| `list-account-tasks`            | `title`, `accountName`, `status`, `type`, `tags`, `balance`, `createdAt`, `updatedAt`                                                                                                                                             |

Sur `list-invoices` et `list-credit-notes`, `tags` porte sur les étiquettes du **compte** auquel le
document appartient, pas sur des étiquettes du document, qui n'en a aucune. Il prend des
**identifiants** d'étiquettes, ceux que renvoie `list-tags`, jamais leur libellé. C'est ainsi qu'un
groupe de sociétés se lit en un appel : étiquetez les comptes une fois, puis
`{ "field": "tags", "operator": "containsAny", "values": ["<identifiant d'étiquette>"] }` renvoie
toutes les factures ouvertes du groupe entier, au lieu d'un appel par compte.

Les mêmes conditions existent sur l'API REST publique, où elles voyagent dans une seule chaîne de
requête. Voyez [Pagination et filtres](/fr/guides/pagination/#filtrer-une-liste) pour le tableau des
opérateurs et la sémantique exacte.

## Crédits

`get-organization` et `list-organizations` renvoient un champ `credits` : le **solde prépayé qui
paie les relances par SMS et par courrier** que Billabex envoie pour votre compte, ainsi que les
recherches de contact. Les emails sont gratuits et ne le consomment jamais.

```json
{
  "id": "123e4567-e89b-12d3-a456-426614174000",
  "name": "Acme Corporation",
  "credits": 250,
  "creditLots": [
    {
      "grantedAt": "2026-01-12T09:30:00.000Z",
      "expiresAt": "2027-01-12T09:30:00.000Z",
      "remaining": 100
    },
    {
      "grantedAt": "2026-02-12T09:30:00.000Z",
      "expiresAt": "2027-02-12T09:30:00.000Z",
      "remaining": 150
    }
  ]
}
```

À zéro, les SMS et les courriers cessent de partir ; les relances par email continuent. Rien d'autre
dans l'API ne le signale, et c'est pourquoi le solde mérite d'être lu si vous construisez un tableau
de bord ou une alerte au-dessus de Billabex.

**Les crédits expirent douze mois après leur chargement, et chaque recharge court sur sa propre
horloge.** C'est ce que rapporte `creditLots`, l'échéance la plus proche d'abord : le solde ci-dessus
annonce 250 crédits, mais 100 d'entre eux disparaissent en janvier. Un envoi dépense toujours
d'abord les crédits les plus proches de leur expiration : une alerte fondée sur le seul solde
manquera donc la chute. Il n'existe aucune notification d'expiration : lisez `creditLots`.

Ces champs sont en **lecture seule**, mais `send-outgoing-message` dépense le solde après avoir rendu
les factures et les avoirs sélectionnés, tout comme `search-contact-candidates` et `enrich-contact`.
Appelez d'abord `preview-outgoing-message` pour obtenir le prix exact au segment ou à la page sans
rien dépenser, puis passez cette valeur en `maxCredits`. Réutilisez la même `idempotencyKey` à chaque
nouvelle tentative. `send-outgoing-communication` envoie toujours un email gratuit. La recharge n'est
pas en libre-service : Billabex la traite manuellement.

## Pagination

Les outils qui renvoient une liste paginent par curseur :

- **Paramètres** : `first` (éléments par page, 20 par défaut, 100 au maximum) et `after` (curseur).
- **Réponse** : un tableau `nodes` et un `pageInfo.endCursor`.
- **Tri** : `list-accounts`, `list-aging-balance-by-account`, `list-invoices`, `list-credit-notes`,
  `list-communications` et `list-account-tasks` prennent `sortBy` et `sortOrder` (`asc` ou `desc`).
  `sortBy` accepte les mêmes valeurs que la liste REST correspondante, énumérées dans la description
  de chaque outil.

Sur une liste filtrée, un `endCursor` appartient à l'ensemble exact de filtres et au tri pour
lesquels il a été émis. Renvoyez-le tel quel pour obtenir la page suivante ; dès qu'une condition ou
le tri change, abandonnez-le et relisez la liste depuis sa première page, faute de quoi l'appel est
refusé.

## Remarques

- OAuth est obligatoire pour tout accès MCP.
- Les jetons doivent être envoyés dans l'en-tête `Authorization: Bearer <jeton>`.
- La limitation de débit s'applique aux requêtes MCP : 300 par minute et par jeton.
- `send-outgoing-communication` et `update-outgoing-communication` vérifient que chaque pièce
  appartient à l'organisation du compte. Un identifiant présent dans une tâche ou un ancien email
  ne suffit pas. Si un ancien fichier ne peut pas être vérifié, ajoutez-le à nouveau via `files`.
- `update-outgoing-communication` accepte un `message` en markdown et de nouvelles pièces jointes
  dans `files`, en base64. Un appel portant des fichiers n'est pas idempotent. N'incluez pas de
  signature : la plateforme produit le HTML et le texte, et ajoute la signature de l'agent
  automatiquement.

### Promesse de paiement

Les lectures exposent aussi `dateEvidence`, nul si aucune provenance n'a été
conservée. Il contient `sourceEmailMessageId`, `authorEmail`, `receivedAt` et
`quote` : message source, adresse de l'auteur, instant de réception et citation
vérifiée. Ce champ est fourni par le serveur, pas par les paramètres d'écriture.
La webapp permet de consulter cette source sur le compte et dans l'historique des
promesses. Une citation prouve sa provenance, jamais la réception d'un paiement
ni, à elle seule, la validité du calcul de date. Les anciens enregistrements ne
reçoivent pas de citation reconstituée.

Les lectures de promesse exposent `promisedDatePrecision` : `exact`,
`conservative-window-end` ou `not-stated`. Une borne de fenêtre ou une date
calculée sans jour annoncé n'est pas un engagement exact. Les données historiques
sans précision sont lues comme `exact`. Ce champ est indépendant de
`amountIsEstimated`. L'outil existant `record-account-payment-promise` et le
`POST /accounts/{accountId}/payment-promise` acceptent le même paramètre facultatif
`promisedDatePrecision`. L'omettre conserve la précision actuelle lors d'une
correction, et donne `exact` sur une création. Utiliser `exact` seulement pour un
engagement explicite ; `conservative-window-end` indique une fin de période
estimée et `not-stated` une date calculée sans jour annoncé. Les deux estimations
exigent `promisedDate` et ne remplacent pas un engagement exact actif. La réponse
renvoie l'objet effectivement conservé, à relire après écriture. La webapp offre
les mêmes choix et préremplit la précision lors d'une correction.

`get-account-dunning-status` renvoie la promesse en cours sous `paymentPromise` (`null` quand il n'y
en a pas, y compris quand le compte ne doit rien), tandis que `record-account-payment-promise` et
`cancel-account-payment-promise` l'écrivent. Ensemble, ils donnent les mêmes capacités que la webapp
et l'API publique (`GET`, `POST` et `DELETE` sur `/accounts/{accountId}/payment-promise`). La
lecture demande `mcp:read`, les écritures `mcp:write` et les droits du compte.

Une promesse est un engagement à payer, lu dans ce que le client a dit : elle ne change aucune
écriture comptable, c'est ce que la relance suivante ne doit pas contredire et ce que lit la
prévision d'encaissement pour les engagements exacts seulement. Une estimation ne produit ni
bonus de promesse ni rupture d'engagement ; son expiration demande une vérification. Un compte
n'en porte qu'une à la fois, donc enregistrer la suivante remplace la précédente, sauf lorsqu'une
estimation rencontre un engagement exact actif : c'est aussi le chemin de modification. Une date déjà passée est refusée,
un paiement que le client dit déjà fait est une déclaration de paiement.

`invoiceNumbers` porte les factures que l'engagement nomme, jamais celles devinées du solde. Sans
montant, la promesse est estimée à ce que ces factures doivent encore, et au solde entier quand
l'engagement n'en nomme aucune ; `amountIsEstimated` le signale. La portée reste par défaut toute
facture échue au jour de l'enregistrement : les numéros la précisent, ils ne la remplacent pas.

### Paiements annoncés et rapprochement

`list-account-payment-declarations`, `record-account-payment-declaration` et `resolve-account-payment-declaration` donnent les mêmes capacités que la webapp et l'API. La lecture demande `mcp:read`, les écritures `mcp:write` et les droits du compte. Une annonce se déduplique par `sourceRef`, et l'annonce d'un paiement déjà retenu (exactement les mêmes factures, un autre `sourceRef`, aucun montant, devise ou date de paiement contradictoire) est repliée dans cette déclaration sous `restatements` au lieu d'en créer une seconde ; résoudre avec son `sourceRef` tranche la déclaration qui l'a absorbée. `declaredStage` reprend ce que le client a dit de son paiement sans le monter en grade, parmi `authorized`, `processed`, `sent` et `paid` : seul `paid` affirme le paiement lui-même, les trois autres annoncent une étape qui peut encore ne rien produire, et la relance demande alors où en est cette étape. `proofDocumentAttached` signale que le client a joint un justificatif de ce règlement : il ne prouve pas la réception et ne change pas le stade, il dit que le rapprochement part d'une référence. Résoudre demande une décision explicite, la note est facultative ; `PartiallyReceived` suppose le montant total payé déjà fixé sur les factures ; ne jamais déduire `PaymentNotReceived` d'une facture ouverte. `PaymentReceived` confirme le montant annoncé, y compris un acompte : la vérification se termine si les montants payés le couvrent dans la devise du compte, sur toutes les factures identifiées et sans autre annonce concurrente. Un solde restant dû ne suffit plus à la maintenir ouverte ; un rapprochement incertain conserve la protection. La clôture de la tâche seule ne résout rien.

## Pour aller plus loin

- **[Authentification OAuth](/fr/guides/oauth/)**
- **[Scopes OAuth](/fr/guides/scopes/)**
- **[Pagination et filtres](/fr/guides/pagination/)**

### Rattachement d'un email entrant

Appelez `get-email` avec `type: "IncomingEmail"`, l'`emailId` (issu de `list-emails`) et
l'`organizationId` : en plus du contenu, `incoming.inboxEntry` renvoie alors `communicationId`,
`organizationId`, `emailId`, `status`, `platformRejection`, `accountId`, `accountFullName`,
`accountDeletedAt` et `taskId`. Les champs de compte et de tâche sont nullables, et `inboxEntry`
lui-même vaut null quand `organizationId` est omis.

`Unclassified` peut déjà avoir un compte résolu sans être une réponse du client utilisée dans les
relances. `PendingReview` porte un compte candidat et `Rejected` un rattachement refusé.
`accountDeletedAt` indique qu'il ne faut plus ouvrir la fiche du compte. L'absence d'entrée dans
l'organisation demandée donne une erreur introuvable ; un accès non autorisé est refusé avant lecture.

L'API publique expose la même lecture :
`GET /api/public/v1/incoming-emails/{emailId}/inbox-entry?organizationId={organizationId}`,
avec les scopes `communications:read` ou `dunning:manage`. Une entrée absente retourne 404. Le nom
du compte y est `accountName`, comme dans `GET /communications`, et non `accountFullName`.

`DeliveryFailure` désigne un avis de non-remise explicite pour au moins un destinataire.
Il peut porter un compte résolu, mais ne constitue pas une réponse du contact et ne participe
pas au contexte des relances. Le détail de l'inbox expose ce statut sur l'API publique et le MCP.

## Décisions BODACC explicites

`ignore-account-bodacc-alert` et `revoke-account-bodacc-alert-override` exigent `mcp:write` et une
décision explicite de l'utilisateur. Lire d'abord le constat détaillé du compte, présenter SIREN,
annonce, procédure, dates et conséquences. Ne jamais inférer la décision d'un acquittement ou
d'une demande générale de reprise. Envoyer `accountId`, `siren`, `noticeId`, `status` et
`confirmed: true`, plus une justification non vide pour ignorer. Les outils appellent le même
service que la webapp et l'API. Lire `applied`, `retryRequired`, `dunningStatus` et `blockingTasks`
avant d'annoncer le résultat. Aucun envoi immédiat ni validation juridique n'est garanti.
Voir [les contrats et exemples](/fr/guides/taches-de-compte/).

Pour ignorer, reprendre les champs `bodaccSiren`, `bodaccNoticeId` et `bodaccStatus` de
`get-account`, avec `canIgnoreBodaccAlert: true` pour une nouvelle décision. `Clear` et
`Unresolved` ne sont pas acceptés. Si le périmètre change, demander une nouvelle confirmation.
Le motif doit contenir de 1 à 2 000 caractères après suppression des espaces périphériques ;
demander un motif plus court s'il dépasse la limite. Pour révoquer, utiliser le périmètre
enregistré dans `bodaccAlertOverride`, même si le constat actuel diffère.

Expliquer le champ `summary`, calculé par le serveur. Si `retryRequired: true` ou
`dunningStatus: null`, l'état des relances reste non vérifié. Une révocation annule l'exception
et ne permet pas d'annoncer une levée du blocage. `applied` ne prouve pas un nouvel événement,
notamment pour une révocation idempotente d'une ancienne décision invalidée.

Copier l'identifiant technique du compte et le motif exactement, en conservant la langue du
motif. Une confirmation explicite déjà donnée après présentation du périmètre reste suffisante
si la relecture ne montre aucun changement. Cette décision appartient à l'utilisateur Billabex,
jamais au débiteur ; ne pas demander au débiteur de confirmer cette exception.

### Confirmer une réception sur des références historiques absentes

Sur `POST /api/public/v1/accounts/{accountId}/payment-declarations/{declarationId}/resolve`,
ou via `resolve-account-payment-declaration`, utiliser une identité utilisateur, la décision
`PaymentReceived`, `historicalInvoicesOutsideBillabex: true` et une `note` personnelle obligatoire
(1 à 4000 caractères, différente des notes par défaut). La note explique la vérification de la
réception complète et du périmètre historique, y compris les variantes éventuelles de numéros.
Un compte de service ne peut pas prendre cette décision.

La réponse expose `invoiceNumbersMissingFromAccount`, `historicalReceiptEligible` et
`historicalReceiptAccountInvoices` (numéro, date, montant des factures actuelles à vérifier).
Le serveur recalcule ces contrôles à la soumission : aucune facture identifiée à l'enregistrement,
au moins une référence, aucune correspondance sur le compte ni dans l'organisation, aucune
lecture en erreur, aucun email préparé en attente sur la tâche. Une décision antérieure différente,
y compris `PaymentReceived` sans le champ, répond au conflit existant.

Le résultat terminal porte `historicalInvoicesOutsideBillabex: true`, l'auteur, la date et la note.
Aucune facture n'est modifiée. Le rejeu identique conserve l'audit et réessaie seulement la clôture
si nécessaire. Les autres déclarations ouvertes retiennent toujours les relances ; sinon elles
peuvent reprendre selon les règles habituelles. L'action n'envoie aucun email. L'absence seule
ne permet jamais de conclure que le paiement est reçu.

### Attente après ajout d'une facture

Une relance automatique attend au minimum 24 heures après l'arrivée de toute facture ouverte
non exclue des relances. Une nouvelle facture reporte aussi la relance consolidée des anciennes.
La date de communication planifiée reste consultable. Quand le réglage de première relance
retient le compte, `firstReminderEligibleAt` inclut ce minimum de 24 heures.

## Importer une facture jointe

Si votre assistant peut transmettre une pièce jointe ChatGPT, utilisez `create-invoice-from-file`.
Le serveur ne le propose qu'à ChatGPT, reconnu à son client OAuth : nommé ChatGPT ou redirigé
vers un domaine OpenAI. Les autres clients, comme Claude ou Microsoft 365 Copilot, ne le voient pas.
Il attend la référence native du fichier, pas du base64. Le champ `file` contient `download_url`
et `file_id`, avec `mime_type` et `file_name` facultatifs.

Sinon, utilisez `prepare-invoice-upload` avec les informations de facture : l'outil renvoie un
lien personnel valable 15 minutes. Ouvrez-le, vérifiez le client, les dates et les montants,
puis choisissez le document et confirmez l'import. Le lien seul ne crée pas la facture.

Dans les deux cas, générez un UUID `importId` et conservez-le avec les mêmes informations pour
les réessais et le changement de parcours. Un réessai réussi renvoie le même reçu. En cas de
facture déjà existante, consultez-la plutôt que modifier son numéro. Les fichiers PDF, PNG,
JPEG et WebP sont limités à 10 Mo. L'outil `create-invoice` reste disponible pour les clients
programmatiques capables de fournir du base64.

Les téléchargements natifs sont limités aux hôtes `oaiusercontent.com` et leurs sous-domaines;
un fichier provenant d'un autre hôte doit passer par le lien de dépôt. La disponibilité du
transfert dépend du client : utilisez le lien de dépôt si ChatGPT ou Work ne transmet pas de
référence téléchargeable. La compatibilité effective doit être testée avec le client utilisé.

## Microsoft 365 Copilot

Billabex peut fournir ses données et ses actions à **Microsoft 365 Copilot**, avec un compte
professionnel ou scolaire. Chaque utilisateur se connecte à son compte Billabex et conserve ses
droits sur les organisations et les clients.

**Disponibilité :** le package de l'agent Billabex est préparé pour un pilote. Il n'est pas encore
publié dans l'Agent Store. Aucun lien public d'installation n'est disponible à ce stade.

### Utiliser Billabex dans Microsoft 365 Copilot

Une fois l'agent activé par votre administrateur Microsoft 365 :

1. Ouvrez Microsoft 365 Copilot et sélectionnez l'agent **Billabex**.
2. Cliquez sur la demande de connexion à Billabex.
3. Connectez-vous à votre compte Billabex et vérifiez les autorisations demandées.
4. Posez votre question, par exemple « Quelles factures clients sont en retard ? » ou
   « Prépare mon briefing du jour avec les réponses récentes et les tâches à traiter ».

Une demande d'analyse consulte les données. Les modifications et les envois d'emails nécessitent
une instruction explicite. Les destinataires et le contenu d'un email sont présentés avant
validation. Pour importer une facture jointe, l'agent fournit un lien personnel de dépôt Billabex.

L'accès aux agents dépend des licences ou de la facturation à l'usage de votre organisation
Microsoft 365 et de ses règles d'administration. Il faut également un compte Billabex autorisé.
La disponibilité de l'agent dans une organisation ne donne aucun droit supplémentaire dans Billabex.

### Préparer un pilote avec votre administrateur

Le package utilise le MCP Billabex existant : **Streamable HTTP**, OAuth et découverte des outils
à chaque session. Il n'ajoute pas de copie des données dans Microsoft Graph.

Votre administrateur ou votre équipe d'intégration peut configurer l'agent avec
[Microsoft 365 Agents Toolkit](https://learn.microsoft.com/en-us/microsoft-365/copilot/extensibility/build-mcp-plugins) :

1. Créer un **Declarative Agent** et ajouter une action **Start with an MCP Server**.
2. Utiliser l'URL `https://next.billabex.com/mcp` de l'environnement Billabex concerné.
3. Choisir **OAuth (with dynamic registration)**. Le serveur publie les métadonnées OAuth et
   l'endpoint d'enregistrement : aucun identifiant OAuth à créer manuellement.
4. Activer l'agent dans un tenant de test, vérifier la connexion et les outils, puis le proposer
   aux utilisateurs autorisés selon les règles de l'organisation.

Le pilote vérifie notamment la connexion initiale, le renouvellement de session, les autorisations,
la sélection de l'organisation, la pagination et la confirmation des actions. Un test d'envoi doit
utiliser un destinataire de test expressément approuvé.

Le parcours OAuth de Microsoft utilise le callback
`https://teams.microsoft.com/api/platform/v1.0/oAuthRedirect`. Utilisez PKCE S256 pour ce parcours.
La découverte des métadonnées se fait sur `https://next.billabex.com/.well-known/oauth-authorization-server`.
Les outils de lecture demandent `mcp:read`, ceux de modification `mcp:write`.

### Distribution à plusieurs entreprises

Pour proposer une installation standard aux clients de plusieurs organisations, Billabex doit
soumettre l'agent via **Microsoft Partner Center**, dans le programme Microsoft 365 et Copilot,
puis passer la revue Microsoft. Le déploiement dans un tenant pilote ne constitue pas une
publication publique. Les administrateurs clients gardent le contrôle des agents disponibles.

Consultez le [circuit de distribution Microsoft](https://learn.microsoft.com/en-us/microsoft-365/copilot/extensibility/publish).

### Copilot grand public

Le Copilot avec un compte Microsoft personnel est une autre expérience. Microsoft documente des
connecteurs pour OneDrive, Outlook et les services Google, mais aucun parcours public permettant
d'ajouter librement un MCP tiers comme Billabex. Installer l'agent Microsoft 365 ne l'ajoute donc
pas au Copilot grand public.

Consultez les [connecteurs du Copilot grand public](https://support.microsoft.com/en-us/microsoft-copilot/connecting-microsoft-copilot-to-other-services).

### Pour aller plus loin

- [Authentification OAuth](/fr/guides/oauth/)
- [Scopes OAuth](/fr/guides/scopes/)

### Détenteurs d'un blocage de facture

`dunningBlock.holders`, lorsqu'il est présent, détaille les causes indépendantes :
`source` (`user` ou `task:<id>`), `conditionKind`, `note`, `blockedAt` et `blockedBy`.
`blockedBy` est nul pour une retenue de tâche. Les champs historiques résument
l'exclusion humaine si elle existe, sinon une retenue de tâche. Une ancienne réponse
sans `holders` représente une exclusion humaine unique.
La levée manuelle (`set-invoice-dunning-block` avec `blocked: false`, ou
`DELETE /api/public/v1/invoices/:invoiceId/dunning-block`) retire seulement la cause
humaine et ne lève pas une condition indépendante liée à une tâche.


Lorsque toutes les factures ouvertes sont retenues uniquement par des tâches, `silenceReason` vaut `invoice-decision-pending`. Une décision ou une résolution vérifiée est attendue ; une date passée ne lève pas ces retenues. Les exclusions manuelles restent distinctes. Ce statut est partagé par la webapp, l’API publique et `get-account-dunning-status`.


### Demander une recharge de crédits

Après accord explicite de l'utilisateur, appeler `update-organization` avec uniquement
`organizationId` et `creditTopUpRequest: { "credits": 200, "requestId": "UUID-v4" }`.
La quantité est un multiple de 100, entre 100 et 2 147 483 600, à 99 € HT par lot de 100.
Réutiliser le même `requestId` pour réessayer une demande identique après une erreur réseau.

L'équivalent REST est `POST /organizations/{organizationId}/credit-top-up-requests`, sous
`/api/public/v1`, avec le scope `organizations:all` et un corps `{ "credits": 200,
"requestId": "UUID-v4" }`. La réponse `200` vaut `true` après acceptation de l'email.
Le MCP renvoie les informations de l'organisation et `creditTopUpRequest` avec `accepted: true`,
la quantité et la référence. L'accusé part à l'utilisateur authentifié, avec
`billing@billabex.com` en copie. Billabex traite ensuite la recharge manuellement : la demande
ne déclenche ni paiement ni ajout de crédits.

---

Billabex developer portal. OpenAPI specification: https://developer.billabex.com/openapi.json.
Agent instructions: https://developer.billabex.com/llms.txt. Complete documentation: https://developer.billabex.com/llms-full.txt.
