---
title: "Tâches de compte et interactions"
description: "Répondre aux tâches ouvertes par l'agent Billabex : rechercher, lister, consulter, annuler, mettre de côté, et ajouter une interaction."
canonical: https://developer.billabex.com/fr/guides/taches-de-compte/
lang: fr
alternate: https://developer.billabex.com/en/guides/account-tasks/
last-updated: 2026-10-27
---

# Tâches de compte et interactions

> Répondre aux tâches ouvertes par l'agent Billabex : rechercher, lister, consulter, annuler, mettre de côté, et ajouter une interaction.

Source: https://developer.billabex.com/fr/guides/taches-de-compte/
Langue: Français (fr)
Version anglaise: https://developer.billabex.com/en/guides/account-tasks/

Une tâche de compte représente dans Billabex une **action à mener** qui demande une intervention ou une approbation humaine. Elles sont **créées exclusivement par l'agent Billabex**, quand il a besoin d'une consigne, d'une coordonnée de contact ou d'une approbation explicite pour poursuivre le suivi d'un compte.

Ce guide couvre leur usage par l'API : lister, consulter, annuler, mettre de côté et répondre par une interaction.

Les horodatages des tâches, dans les listes comme dans le détail, sont des instants ISO 8601
en UTC avec le suffixe `Z` (par exemple `2026-10-19T16:34:00.123Z`). Conservez ce fuseau lors
du calcul d'une durée ; convertissez dans le fuseau de l'utilisateur uniquement pour l'affichage.

## Vue d'ensemble

Une tâche de compte suit un cycle de vie simple :

1. **Créée** : l'agent ouvre une tâche quand une intervention est nécessaire
2. **Ouverte** : la tâche attend une réponse
3. **Interaction** : un utilisateur répond par un message
4. **Close ou annulée** : la tâche est résolue par l'agent, ou annulée par l'utilisateur

Chaque tâche porte un **type**, qui détermine la forme de réponse attendue.

> **Note** : seul l'agent Billabex peut créer une tâche de compte. L'API publique permet de les lire, d'y répondre, de les mettre de côté et de les annuler, pas d'en créer.

## Types de tâches

| Type                 | Description                                                        | Réponse attendue           |
| -------------------- | ------------------------------------------------------------------ | -------------------------- |
| `ApproveEligibility` | Approuver ou refuser un contact pour le suivi                      | Structurée : `boolean`     |
| `NeedContacts`       | Fournir les coordonnées d'un contact pour un compte                | Structurée : objet contact |
| `AskNextAction`      | Choisir la prochaine action                                        | Choix structuré            |
| `NeedUserInput`      | L'agent a besoin d'une précision ou d'une information              | Message texte              |
| `Unknown`            | Type de repli                                                      | Structurée : `boolean`     |
| `Notice`             | L'agent signale une action qu'il a déjà faite ; rien n'est attendu | Aucune                     |

### `extraData.idempotencyKey`

Les types de tâches qui ne portent pas de charge structurée propre (`Notice`, `AskNextAction`,
`NeedContacts`, `ApproveFirstOutreach`, `Unknown`) exposent `extraData.idempotencyKey`. C'est une
clé stable que l'agent pose quand plusieurs tâches d'un même type peuvent légitimement coexister sur
un compte, et elle vaut `null` sinon. Elle ne change pas quand la tâche est rejouée : utilisez-la
pour reconnaître une tâche déjà vue. Un `Notice` signalant une réclamation client est par exemple
clé par `contact-grievance:<identifiant de l'email entrant>`.

## Scopes nécessaires

| Scope        | Niveau d'accès | Endpoints                                                                                                                                                                   |
| ------------ | -------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `tasks:read` | Lecture seule  | `GET /account-tasks`, `GET /account-tasks/search`, `GET /account-tasks/:accountTaskId`                                                                                      |
| `tasks:all`  | Accès complet  | Tout le précédent + `POST /account-tasks/:accountTaskId/cancel`, `POST /account-tasks/:accountTaskId/close`, `POST /account-tasks/:accountTaskId/set-pending`, interactions |

## Lister les tâches actives

Récupérer les tâches actives d'un compte précis.

```http
GET https://next.billabex.com/api/public/v1/account-tasks?accountId=ACCOUNT_ID
Authorization: Bearer YOUR_ACCESS_TOKEN
```

Paramètres de requête :

| Paramètre   | Type   | Obligatoire | Description                        |
| ----------- | ------ | ----------- | ---------------------------------- |
| `accountId` | UUID   | oui         | Restreindre les tâches à ce compte |
| `type`      | chaîne | non         | Restreindre à un type de tâche     |

Exemple de réponse :

```json
[
  {
    "task": {
      "id": "123e4567-e89b-12d3-a456-426614174000",
      "organizationId": "org-uuid",
      "accountId": "account-uuid",
      "type": "NeedUserInput",
      "title": "Clarification needed on invoice #1234",
      "status": "UserActionRequired",
      "isActive": true,
      "isClosed": false,
      "isCanceled": false,
      "thread": [
        {
          "id": "interaction-uuid",
          "message": {
            "type": "Text",
            "value": "The customer mentioned a dispute. How should I proceed?"
          },
          "attachments": [],
          "sentAt": "2024-05-01T10:15:30.000Z",
          "agent": {
            "email": "agent@example.com",
            "firstName": "Alex",
            "lastName": "Martin"
          }
        }
      ],
      "assignees": [],
      "createdAt": "2024-05-01T10:15:30.000Z",
      "closedAt": null,
      "canceledAt": null,
      "cancelReason": null,
      "canceledBy": null,
      "completedAt": null,
      "isPending": false,
      "pendingAt": null,
      "pendingUntil": null,
      "extraData": { "refEmailMessageIds": [] }
    }
  }
]
```

## Rechercher des tâches de compte

`GET /account-tasks` répond pour un seul compte et renvoie ses tâches actives d'un coup. Pour
travailler à l'échelle de l'organisation, avec filtres, tri et pagination, utilisez l'endpoint de
recherche.

```http
GET https://next.billabex.com/api/public/v1/account-tasks/search?organizationId=ORG_ID&first=20
Authorization: Bearer YOUR_ACCESS_TOKEN
```

Paramètres de requête :

| Paramètre        | Type   | Obligatoire | Description                                                                                    |
| ---------------- | ------ | ----------- | ---------------------------------------------------------------------------------------------- |
| `organizationId` | UUID   | oui         | Organisation dans laquelle chercher                                                            |
| `accountId`      | UUID   | non         | Restreindre à un seul compte                                                                   |
| `query`          | chaîne | non         | Recherche en texte libre sur le titre de la tâche et le nom du compte                          |
| `filters`        | chaîne | non         | Conditions de filtrage, voir [Pagination et filtres](/fr/guides/pagination/#filtrer-une-liste) |
| `sortBy`         | chaîne | non         | `createdAt`, `updatedAt`, `title`, `accountFullName`, `status` ou `balance`                    |
| `sortOrder`      | chaîne | non         | `asc` ou `desc`                                                                                |
| `first`          | entier | non         | Entre `1` et `100`                                                                             |
| `after`          | chaîne | non         | Curseur issu de `pageInfo.endCursor`                                                           |

Champs filtrables :

| Champ         | Opérateurs                                                              |
| ------------- | ----------------------------------------------------------------------- |
| `title`       | `contains`, `startsWith`, `equals`                                      |
| `accountName` | `contains`, `startsWith`, `equals`                                      |
| `status`      | `is`, `isNot`, `isAnyOf`                                                |
| `type`        | `is`, `isNot`, `isAnyOf`                                                |
| `tags`        | `containsAny`, `containsAll`, `containsNone`, `isEmpty`, `isNotEmpty`   |
| `balance`     | `equals`, `greaterThan`, `lessThan`, `between`, `isEmpty`, `isNotEmpty` |
| `createdAt`   | `on`, `before`, `after`, `between`                                      |
| `updatedAt`   | `on`, `before`, `after`, `between`, `isEmpty`, `isNotEmpty`             |

`type` accepte `ApproveEligibility`, `ApproveContactChange`, `ApprovePaymentArrangement`,
`ApproveFirstOutreach`, `NeedContacts`, `AskNextAction`, `NeedUserInput`,
`PaymentScheduleInvalidated`, `Notice` et `Unknown`. `status` accepte les valeurs du tableau plus
bas dans cette page.

Les tâches ouvertes d'un type donné, les plus récentes d'abord :

```http
GET https://next.billabex.com/api/public/v1/account-tasks/search?organizationId=ORG_ID&filters=status%3Ais%3AUserActionRequired%3Btype%3Ais%3ANeedContacts&sortBy=createdAt&sortOrder=desc
```

Les tâches créées depuis le début du trimestre, sur des comptes devant plus de 1000 :

```http
GET https://next.billabex.com/api/public/v1/account-tasks/search?organizationId=ORG_ID&filters=createdAt%3Aafter%3A2026-06-30%3Bbalance%3AgreaterThan%3A1000
```

Exemple de réponse :

```json
{
  "nodes": [
    {
      "id": "123e4567-e89b-12d3-a456-426614174000",
      "organizationId": "org-uuid",
      "accountId": "account-uuid",
      "accountFullName": "Acme Corporation",
      "accountTagIds": [],
      "title": "Clarification needed on invoice #1234",
      "type": "NeedUserInput",
      "status": "UserActionRequired",
      "assignees": [],
      "assigneesFullName": [],
      "isActive": true,
      "isClosed": false,
      "isCanceled": false,
      "isPending": false,
      "balance": 1250.5,
      "currency": "EUR",
      "createdAt": "2026-05-01T10:15:30.000Z",
      "updatedAt": "2026-05-02T08:00:00.000Z",
      "closedAt": null,
      "canceledAt": null,
      "cancelReason": null,
      "canceledBy": null,
      "pendingAt": null,
      "pendingUntil": null,
      "completedAt": null,
      "lastAgentInteractionAt": "2026-05-01T10:15:30.000Z"
    }
  ],
  "pageInfo": {
    "endCursor": "v2:8f14e45fceea:2"
  }
}
```

Deux points à garder en tête :

- Les éléments ne portent pas le fil d'interactions `thread`. Appelez
  `GET /account-tasks/:accountTaskId` pour le fil d'une tâche.
- Le `endCursor` appartient aux filtres et au tri pour lesquels il a été émis. Changer l'un ou
  l'autre l'invalide, et l'appel renvoie `400` avec le code `LIST_CURSOR_INVALID`.

## Consulter une tâche

Récupérer une tâche par son identifiant.

```http
GET https://next.billabex.com/api/public/v1/account-tasks/:accountTaskId
Authorization: Bearer YOUR_ACCESS_TOKEN
```

Renvoie la même structure que plus haut, enveloppée dans `{ "task": { ... } }`.

## Annuler une tâche

Annuler une tâche active la marque comme annulée sans la résoudre.

```http
POST https://next.billabex.com/api/public/v1/account-tasks/:accountTaskId/cancel
Authorization: Bearer YOUR_ACCESS_TOKEN
Content-Type: application/json

{
  "reason": "Facture renvoyée à la main, le contact a confirmé la réception."
}
```

Le corps est facultatif, et `reason` aussi (2000 caractères au plus). En fournir une est
fortement recommandé : l'annulation est la seule issue qui ne répond à rien, et plusieurs types de
tâches retiennent les relances automatiques tant qu'elles sont actives. En annuler une rend donc la
cadence à l'agent. Le motif est affiché à côté de l'annulation, et l'agent le relit avant de
rouvrir le même sujet sur ce compte.

Renvoie la tâche mise à jour, avec `isCanceled: true`, `canceledAt` renseigné, `cancelReason`
portant le motif s'il a été donné, et `canceledBy` nommant qui a annulé et par quelle porte :

```json
{
  "canceledBy": {
    "userId": "usr_01H8...",
    "userFullName": "Camille Roy",
    "channel": "PublicApi"
  }
}
```

Une vérification de paiement ne s'annule pas : une tâche dont `extraData.paymentDeclarationId` est
renseigné répond `400` avec le code `app-tasking.account-task.payment-review-requires-decision` et
`details.paymentDeclarationId`. Enregistrez la décision sur la déclaration (voir
[Paiements annoncés](#paiements-annoncés)) : elle clôture la vérification.

`canceledBy` vaut `null` quand c'est l'agent qui a annulé, et sur les annulations enregistrées avant
le 2026-09-03. `channel` vaut `Webapp`, `PublicApi` ou `Mcp`, et `null` sur une annulation
enregistrée avant que la porte soit suivie. `userFullName` vaut `null` quand l'utilisateur n'a pas
pu être nommé, typiquement après son départ de l'organisation.

## Terminer une tâche

Clôturer une tâche dont le sujet a été traité ailleurs, sans rien demander à l'agent : aucun modèle
ne décide de la clôture et aucun email n'est écrit pour elle.

```http
POST https://next.billabex.com/api/public/v1/account-tasks/:accountTaskId/close
Authorization: Bearer YOUR_ACCESS_TOKEN
```

Le corps est vide ; un champ envoyé est refusé en `400`. L'auteur est l'utilisateur du jeton.
Seule une tâche `NeedUserInput` qui demande une information se termine ainsi, et pas pendant qu'une
de vos réponses est en cours de traitement (`InProgress`) :

| Refus (`400`)                                               | Cas                                                                                      |
| ----------------------------------------------------------- | ---------------------------------------------------------------------------------------- |
| `app-tasking.account-task.payment-review-requires-decision` | vérification de paiement : enregistrer la décision sur la déclaration                    |
| `app-tasking.account-task.closure-not-allowed`              | autre type de tâche, correction annoncée ou contestation : répondre avec l'action prévue |
| `app-tasking.account-task.reply-in-progress`                | une réponse est en cours de traitement : réessayer une fois la réponse de l'agent reçue  |
| `app-tasking.account-task.canceled`                         | la tâche est annulée et le reste                                                         |

Les informations manquantes ne sont pas marquées comme fournies et les pauses restent en place.
La clôture peut lever la retenue des relances que représentait cette tâche : lisez le statut de
relance du compte pour le savoir. Renvoie la tâche avec `isClosed: true`, `closedAt` et `closedBy`
(même forme que `canceledBy`). Renvoyer la même clôture rend la tâche fermée, avec son premier
auteur. `closedBy` vaut `null` sur une clôture décidée par l'agent.

Les tâches ordinaires `NeedUserInput` qui référencent un email, ainsi que les revues de date
estimée dont la clé commence par `estimated-payment-date-review:`, refusent `/close` et
`/cancel` avec `app-tasking.account-task.closure-not-allowed`. Utilisez la décision motivée
ci-dessous. Un brouillon lié exige aussi sa décision dédiée avant fermeture ou annulation.
L'annulation d'une revue de destinataires reste possible et conserve sa pause.

### Décision motivée pour une demande ordinaire

Pour conserver la raison et les exigences abandonnées, envoyez une interaction à
`POST /api/public/v1/account-tasks/:accountTaskId/interactions` :

```json
{
  "message": {
    "type": "Structured",
    "schema": "ResolveNeedUserInput",
    "schemaVersion": 1,
    "value": {
      "decision": "handled-elsewhere",
      "reason": "Documents supplied directly to the debtor",
      "waivedRequirementIds": ["purchase-order"]
    }
  },
  "attachments": []
}
```

Relisez d'abord la tâche. `waivedRequirementIds` doit contenir exactement les identifiants de
ses exigences actuelles : `null` si le contrat est inconnu, `[]` s'il est vide. Une sélection
périmée, partielle ou dupliquée est refusée. Les décisions possibles sont `handled-elsewhere`
(traité ailleurs), `no-longer-needed` (devenu inutile) et `continue-follow-up` (poursuivre le suivi).
Le motif est obligatoire, de 1 à 4 000 caractères après suppression des espaces extérieurs.

La décision, le reçu et la clôture sont enregistrés ensemble. L'abandon n'est pas une preuve de
réception d'un document ou d'un paiement. Aucun email n'est envoyé ; les autres tâches, litiges
et pauses restent inchangés. Les pièces jointes et les tâches disposant d'une décision dédiée
ne sont pas acceptées dans ce parcours. Une répétition identique par le même utilisateur
retrouve l'interaction existante. L'API renvoie cette interaction ; relisez le détail de la tâche
pour consulter son reçu et son état.

En MCP, utilisez le paramètre `resolution` de `close-account-task`, avec le contenu de `value`
ci-dessus. L'outil renvoie l'état de la tâche relu après enregistrement.

## Mettre une tâche de côté

Reporter une tâche plutôt que d'y répondre tout de suite, en nommant éventuellement la date à
laquelle elle doit revenir.

```http
POST https://next.billabex.com/api/public/v1/account-tasks/:accountTaskId/set-pending
Authorization: Bearer YOUR_ACCESS_TOKEN
Content-Type: application/json

{
  "pendingUntil": "2026-09-20T08:00:00.000Z"
}
```

Le corps est facultatif, et `pendingUntil` aussi, qui doit être un instant ISO 8601 dans le futur.
Fournissez-le dès que vous savez quand la réponse sera disponible : à cette date, Billabex publie un
message d'agent sur la tâche expliquant qu'elle est de retour, et la tâche repasse d'elle-même en
`UserActionRequired`.

Sans lui, la tâche revient quand même, au bout de 30 jours. **Une tâche ne reste jamais de côté
indéfiniment**, et cela compte parce que plusieurs types de tâches retiennent les relances
automatiques de leur compte tant qu'elles sont actives : reporter une tâche repousse la décision, il
ne rend pas la cadence. Annulez la tâche ou répondez-y pour cela.

Renvoie la tâche mise à jour, avec `isPending: true`, `pendingAt` renseigné, et `pendingUntil`
portant la date si elle a été donnée.

## Ajouter une interaction

Répondre à une tâche en y ajoutant une interaction utilisateur. C'est ainsi que l'on fournit à
l'agent Billabex l'information qu'il demande.

```http
POST https://next.billabex.com/api/public/v1/account-tasks/:accountTaskId/interactions
Authorization: Bearer YOUR_ACCESS_TOKEN
Content-Type: application/json
```

### Interaction texte

Pour les tâches `NeedUserInput` :

```json
{
  "interaction": {
    "message": {
      "type": "Text",
      "value": "Proposez un échéancier sur 3 mois avec 10 % d'intérêts."
    },
    "attachments": []
  }
}
```

### Interaction structurée

Pour les tâches `ApproveEligibility`, `NeedContacts` et `Unknown`, utilisez un message structuré.

#### AskNextAction

Le choix `resume` accepte `instructions` (2 000 caractères maximum) pour le prochain email uniquement. Avec une consigne non vide, les relances restent en pause et un brouillon est préparé : lisez `extraData.pendingCommunicationId`, puis la communication complète. Soumettez ensuite une interaction `DecideOutgoingEmailDraft` v1 sur cette tâche avec `{ "communicationId": "…", "decision": "send" }` pour approuver l'envoi et demander la reprise. `discard` abandonne le brouillon et conserve la pause. Sans consigne, `resume` conserve sa reprise immédiate. Le MCP expose le même parcours dans `decide-account-task-next-action` puis `decide-account-task-email-draft`, sans outil supplémentaire.

`AskNextAction` exige une réponse structurée : `take-over`, `resume` ou `add-contact`. Ce dernier choix exige `contact.email` et `contact.language` ; `contact.fullName` est facultatif. Les clients qui envoyaient du texte libre reçoivent désormais une erreur HTTP 400 immédiate, sans interaction enregistrée ni changement de statut. Aucun nouveau schéma ni migration.

```json
{
  "interaction": {
    "message": {
      "type": "Structured",
      "schema": "AskNextAction",
      "schemaVersion": 1,
      "value": { "choice": "resume" }
    }
  }
}
```

#### Réponse à ApproveEligibility

```json
{
  "interaction": {
    "message": {
      "type": "Structured",
      "schema": "ApproveEligibility",
      "schemaVersion": 1,
      "value": true
    }
  }
}
```

`value` est un booléen : `true` pour approuver, `false` pour refuser.

#### Réponse à NeedContacts

```json
{
  "interaction": {
    "message": {
      "type": "Structured",
      "schema": "NeedContacts",
      "schemaVersion": 1,
      "value": {
        "fullName": "Jane Doe",
        "email": "jane@company.com",
        "language": "fr"
      }
    }
  }
}
```

Champs obligatoires dans `value` :

- `fullName` (chaîne)
- `email` (chaîne)
- `language` (chaîne, par exemple « en » ou « fr »)

#### Réponse à Unknown

```json
{
  "interaction": {
    "message": {
      "type": "Structured",
      "schema": "Unknown",
      "schemaVersion": 1,
      "value": true
    }
  }
}
```

## Référence des schémas de message

| Type de tâche        | Nom du schéma                                           | Version | Type de valeur                                    |
| -------------------- | ------------------------------------------------------- | ------- | ------------------------------------------------- |
| `ApproveEligibility` | `ApproveEligibility`                                    | 1       | `boolean`                                         |
| `NeedContacts`       | `NeedContacts`                                          | 1       | `{ fullName, email, language }`                   |
| `Unknown`            | `Unknown`                                               | 1       | `boolean`                                         |
| `AskNextAction`      | `AskNextAction`                                         | 1       | `{ choice, contact?, instructions? }`                            |
| `NeedUserInput`      | `DecideOutgoingEmailDraft` pour un brouillon en attente | 1       | `{ communicationId, decision }`, ou message texte |

## Règles de validation

L'API valide strictement les messages d'interaction :

- une tâche `NeedUserInput` accepte le texte, ou `DecideOutgoingEmailDraft` lorsqu'elle porte un
  `pendingCommunicationId` ;
- `AskNextAction` accepte aussi `DecideOutgoingEmailDraft` v1 pour sa référence de brouillon courante ;
- une **tâche structurée** doit recevoir un message `type: "Structured"` au schéma correspondant ;
- le nom et la version du schéma doivent correspondre exactement ;
- la valeur doit respecter le type attendu par ce schéma.

Un message invalide renvoie `400 Bad Request` avec le détail.

Pour valider un email préparé par la tâche, envoyez une interaction structurée avec le même
`communicationId` et `decision: "send"`. `decision: "discard"` abandonne le brouillon. Le succès du
POST prouve l'enregistrement de la décision ; relisez ensuite la communication et vérifiez
`awaitingApproval: false` avant de considérer l'email comme armé. Un texte comme « oui, envoie » ne
valide jamais le brouillon.

## Statuts d'une tâche

| Statut               | Description                                                                                                                                                                       |
| -------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `Open`               | Tâche créée, en attente de traitement                                                                                                                                             |
| `InProgress`         | L'agent traite la dernière réponse. Si ce traitement échoue, la tâche revient en `UserActionRequired` sous 3 heures, avec un message de l'agent, et accepte à nouveau une réponse |
| `UserActionRequired` | En attente d'une réponse utilisateur                                                                                                                                              |
| `Closed`             | Tâche menée à bien                                                                                                                                                                |
| `Canceled`           | Tâche annulée                                                                                                                                                                     |
| `Pending`            | Tâche mise de côté, de retour à `pendingUntil` ou au bout de 30 jours                                                                                                             |

## Erreurs courantes

### 400 Bad Request, message invalide

```json
{
  "statusCode": 400,
  "message": "Text message is required for this task type"
}
```

Cause : un message structuré envoyé à une tâche qui n'accepte que du texte.

### 404 Not Found, tâche introuvable

```json
{
  "statusCode": 404,
  "message": "Account task 123e4567-e89b-12d3-a456-426614174000 not found"
}
```

Cause : l'identifiant n'existe pas, ou la tâche appartient à une autre organisation.

### 403 Forbidden, scope insuffisant

```json
{
  "statusCode": 403,
  "message": "Forbidden",
  "error": "insufficient_scope"
}
```

Cause : votre jeton ne porte pas `tasks:all` pour une opération d'écriture.

## Résolution des protections de relance

Une réponse `NeedUserInput` suit les mêmes règles dans la webapp, l'API et le MCP. Une instruction
n'est pas confirmée ni la tâche clôturée si ses effets n'ont pas pu être appliqués. Fermer, annuler
ou mettre de côté une tâche ne résout pas le paiement ou la correction qu'elle demande de vérifier.

Pour une correction annoncée, mettre à jour les factures concernées puis répondre à la tâche.
Si la correction a été abandonnée, le préciser explicitement et autoriser la reprise sur la facture
d'origine. Un simple « merci » ou une reprise générique ne constitue pas cette décision.
Un paiement annoncé exige une décision explicite, qui peut désormais être enregistrée depuis la
tâche qui lui est liée, comme décrit ci-dessous.

La vérification se clôture automatiquement quand les factures liées sont soldées. Pour une annonce
sans référence de facture, elle se clôture aussi si le compte contient au moins une facture et que
toutes sont individuellement soldées. La décision est `InvoicesSettled`, sans confirmation implicite
de réception du paiement. Un compte sans facture ou une référence introuvable exige toujours une
vérification. Cette règle est commune aux trois surfaces.

Pour une tâche liée à un paiement annoncé, une interaction texte peut porter la décision de
l'utilisateur : la réception (`PaymentReceived`), la non-réception constatée après vérification
(`PaymentNotReceived`) ou la reprise des relances sans se prononcer sur le paiement
(`ResumeAuthorized`). La décision est enregistrée au nom de l'utilisateur, avec les mêmes contrôles
que la décision du compte, et il n'est pas nécessaire de la répéter dans le formulaire. Le
traitement est asynchrone : relire la tâche et les paiements annoncés pour connaître le résultat.
Si les factures concernées restent ouvertes après une réception, celle-ci est enregistrée et les
relances restent retenues jusqu'au rapprochement : pendant le délai laissé au règlement, la tâche
est clôturée ; une fois ce délai écoulé, une tâche demande la mise à jour des factures ou la reprise
des relances. Une tâche de revue annulée ou clôturée n'est pas recréée, sauf une fois à l'échéance
de ce délai si elle avait été ouverte avant. Une demande de clôture seule, un propos rapporté du
client, une promesse ou une demande d'attendre ne décident rien. Aucune facture n'est modifiée par
cette interaction. Ce fonctionnement est commun à la webapp, à l'API publique et au MCP ; les
décisions `CreditApplied`, `InvoicesSettled` et `Withdrawn` utilisent les opérations explicites du
compte.

## Paiements annoncés

Un paiement annoncé n'est pas un paiement comptabilisé. Le registre reste actif après clôture ou annulation de sa tâche. Utiliser le scope `dunning:manage` et les endpoints sous `/api/public/v1/accounts/{accountId}/payment-declarations` :

- `GET` : lire les déclarations et décisions.
- `POST` : enregistrer `sourceRef`, `declaredAt`, et éventuellement `paymentDate`, `amount`, `currency`, `declaredStage`, `invoiceNumbers`, `proofDocumentAttached`. Réutiliser `sourceRef` lors d'une nouvelle tentative.
- `POST /{declarationId}/resolve` : fournir `resolution`, et une `note` facultative. Sans note, une note factuelle dérivée de la décision est enregistrée, à votre nom.

Une tâche de vérification porte `extraData.paymentDeclarationId`, l'identité de la déclaration qu'elle vérifie (opaque, pas forcément un UUID). Chaque déclaration porte `reviewTaskStatus` (`open`, `closed`, `canceled`, `unknown`, `null` sans tâche) et `resolvedByUserFullName`. Une décision à côté d'une vérification encore `open` est enregistrée : sa clôture est en cours de reprise, et rejouer la même décision la retente.

Un paiement déjà retenu n'est pas enregistré deux fois. Une nouvelle déclaration qui nomme exactement les mêmes factures qu'une déclaration non résolue, vient d'un autre `sourceRef`, et dont ni le montant, ni la devise, ni la date de paiement ne se contredisent (égaux, ou donnés d'un seul côté) est repliée dans la déclaration existante au lieu d'en créer une seconde. La déclaration existante complète ce qu'elle ignorait, prend le stade de l'annonce la plus récente et range la nouvelle, telle qu'envoyée, dans `restatements`. La réponse contient alors votre `sourceRef` dans cette liste plutôt que comme une déclaration à part, et résoudre avec lui tranche la déclaration qui l'a absorbé. Un montant, une devise, une date de paiement ou des factures différents, ou une déclaration déjà tranchée, créent toujours une nouvelle déclaration.

`proofDocumentAttached` dit que le client a joint un document qu'il présente comme la preuve de ce règlement : un avis de virement, un reçu bancaire, une capture de la transaction. Le champ ne prouve rien de l'arrivée de l'argent et ne change ni le stade annoncé ni la retenue des relances ; il indique que le rapprochement part d'une référence et non d'une simple affirmation, et la tâche de revue le signale. Une déclaration enregistrée avant l'existence du champ le porte à `false`.

La question de revue s'ouvre dès l'enregistrement de la déclaration, pas à l'expiration de sa période de grâce.

Résolutions : `PaymentReceived`, `PartiallyReceived`, `CreditApplied`, `InvoicesSettled`, `PaymentNotReceived`, `Withdrawn`, `ResumeAuthorized`. `PartiallyReceived` confirme qu'une partie du paiement est arrivée : fixez d'abord le montant total payé de chaque facture concernée (`PUT /invoices/{invoiceId}/paid-amount` avec `expectedPaidAmount`, le montant lu), puis enregistrez la décision. Elle ne modifie aucune facture, exige au moins une facture payée en partie et un reste dû, et les relances reprennent sur le solde restant. La réception confirmée (`PaymentReceived`) porte sur le montant annoncé, y compris un acompte. Elle termine la vérification même si un solde reste dû, lorsque toutes les factures sont identifiées, que leurs montants payés couvrent le montant annoncé dans la devise du compte et qu’aucune autre annonce ne rend cette affectation ambiguë. Sinon, `paymentConfirmedAt` est enregistré et le rapprochement reste attendu. Aucun montant de facture n’est modifié. `ResumeAuthorized` autorise la reprise sans nier le paiement. Un avoir ou une facture soldée ne prouvent pas une réception bancaire. Toutes ces routes renvoient la liste courante des déclarations.

La décision d'une personne est définitive. Rejouer la même décision ne change rien et renvoie la liste inchangée ; en poser une différente répond `400` avec le code `app-dunning.payment-declaration.already-resolved`, dont le champ `details` porte `declarationId`, `resolution`, `resolvedAt` et `resolvedBy`. Afficher cette décision plutôt que l'échec.

Une exception, et une seule : la décision que les données de facturation ont prise seules. Solder les factures d'une déclaration la tranche automatiquement en `InvoicesSettled` dans la seconde qui suit, avec `resolvedBy` à `accounting`. Cette décision-là répond du rapprochement des factures, jamais de l'arrivée de l'argent, et une décision explicite d'utilisateur la remplace sans erreur. C'est le seul chemin par lequel `PaymentReceived` s'enregistre sur une telle déclaration, et `PaymentReceived` est la seule décision qui autorise la relance suivante à remercier le débiteur. Le remplacement est à sens unique : la revue automatique ne revient jamais sur la décision d'une personne, et la vérification des factures s'applique au remplacement comme à une première décision. Relire la déclaration avant de proposer une décision à un utilisateur.

## Pour aller plus loin

- **[Démarrage](/fr/guides/demarrage/)** : le parcours d'intégration rapide
- **[Scopes OAuth](/fr/guides/scopes/)** : les permissions nécessaires
- **[Pagination et filtres](/fr/guides/pagination/)** : curseurs et paramètre `filters`
- **[Limites de débit](/fr/guides/limites-de-debit/)** : éviter de les atteindre
- **[Référence de l'API](/fr/reference-api/)** : documentation complète des endpoints

## Support

Une question sur les tâches de compte et les interactions ?
Écrivez-nous via le [formulaire de contact](https://www.billabex.com/fr/contactez-nous/).

## Confirmation des destinataires des relances

Pour le MCP, fournir `accountTaskId` et `value` uniquement (avec `attachments` si nécessaire). Les exemples ci-dessous montrent le corps API publique ; le MCP fixe lui-même `schema` et `schemaVersion`.

Une tâche `NeedUserInput` portant `extraData.idempotencyKey = "dunning-recipient-review"` bloque les relances automatiques quand plus de cinq contacts joignables sont actifs sans sélection confirmée. Lire les contacts actuels du compte, puis répondre par l’interaction structurée existante (API publique ou outil MCP `confirm-account-task-recipients`) :

```json
{
  "type": "Structured",
  "schema": "ConfirmDunningRecipients",
  "schemaVersion": 1,
  "value": {
    "reviewedContacts": [
      { "id": "contact-id", "email": "contact@example.com" }
    ],
    "selectedContactIds": ["contact-id"],
    "confirmLargeSelection": false
  }
}
```

`reviewedContacts` contient tous les contacts actifs dont l’adresse est valide que l’utilisateur a examinés, avec leurs adresses actuelles. `selectedContactIds` est un sous-ensemble non vide et sans doublons. Au-delà de cinq sélectionnés, `confirmLargeSelection: true` est obligatoire. Les contacts examinés non sélectionnés sont désactivés, sans suppression. La tâche se clôture après enregistrement. Un contact ajouté ou une adresse changée entre la lecture et la validation impose de relire les contacts. Une réponse textuelle ou une clôture générique ne valide pas ce choix.

## Désignation du contact et reprise des relances

Pour une tâche `NeedUserInput` portant `extraData.idempotencyKey = "dunning-recipient-routing"`,
une réponse textuelle comme `hello@example.com` désigne le contact des prochaines relances.
L’agent ajoute ou réutilise ce contact avant la clôture. Les contacts désactivés et les adresses
rejetées restent protégés ; un échec d’enregistrement garde la tâche ouverte. Une adresse seule
ne demande aucun email immédiat et ne vaut pas autorisation de reprendre des relances suspendues.

La clé `dunning-resume-confirmation` identifie une question de reprise. Répondez par une
interaction structurée, via l’endpoint d’interaction structurée ou l’outil MCP
`confirm-account-task-resume` :

```json
{
  "type": "Structured",
  "schema": "ConfirmDunningResume",
  "schemaVersion": 1,
  "value": { "decision": "resume" }
}
```

`decision` vaut `resume` (reprendre les relances) ou `keep_paused` (maintenir la suspension).
Ce schéma est refusé sur toute autre tâche. Les réponses textuelles exactes
`Reprendre les relances`, `Maintenir la suspension`, `Resume reminders` et
`Keep reminders paused` restent reconnues pour compatibilité ; tout autre texte est interprété
par l’agent. La tâche se clôture seulement après le succès de l’action sur le compte.

## Conserver un échéancier existant

Pour `ApprovePaymentArrangement`, envoyer `schema: "DecidePaymentArrangement"`,
`schemaVersion: 1`, `value: { "choice": "keep_existing" }` via l’interaction structurée
(API publique ou MCP). Ne transmettre ni `invoiceIds` ni `installments`.
Le serveur relit tout le périmètre figé : au moins une facture non soldée doit porter
des échéances restant dues. La tâche est clôturée sans modifier les factures ni envoyer
un email. Seule la pause `payment-plan` attribuée à cette tâche est levée.
`grant` reste disponible avec `invoiceIds` et `installments`, et `refuse` sans ces champs.

## Rejets de plateforme

Une tâche `NeedUserInput` peut contenir `extraData.platformRejection` : `platform` (`Basware`
ou `Mailinblack`), `reason`, `sender`, `destination`, `expiresAt`, `sourceEmailIds`,
`outgoingEmailIds`, `resolution`, `resolutionNote`.
Cette portée bloque seulement le couple expéditeur/destination du compte, même après clôture
si la résolution est `exclude`. Les tâches ordinaires conservent leur portée globale.

Une tâche `Mailinblack` signale que la messagerie du destinataire est protégée par une
passerelle de sécurité qui retient la relance tant qu'une personne n'a pas validé l'expéditeur.
L'agent ne suit jamais le lien de validation. La destination est écartée des relances
automatiques jusqu'à `expiresAt`, cinq jours ouvrés après le premier challenge de l'épisode ;
les relances reprennent ensuite même si la tâche reste ouverte, et un challenge ultérieur ne
repousse pas cette date. Une tâche Basware n'a pas d'`expiresAt`.

Utiliser `POST /api/public/v1/account-tasks/{accountTaskId}/resolve-platform-rejection`, avec
le scope OAuth `tasks:all`, ou MCP `resolve-platform-rejection-task` avec `mcp:write` :

```json
{ "resolution": "exclude", "note": "Utiliser le contact comptable confirmé." }
```

MCP attend en plus `accountTaskId`. `exclude` conserve l'exclusion ; `retry` autorise un
nouvel essai après résolution du problème. Sur une tâche `Mailinblack`, `retry` signifie que
l'adresse de l'agent est désormais autorisée et demande aussi de planifier la prochaine relance
(une replanification, pas un envoi immédiat). La note est obligatoire (1 à 2 000 caractères).
La commande exige un utilisateur autorisé et n'envoie aucun email. Une clôture, annulation,
suppression ou réponse libre générique est refusée. Consulter l'entrée brute via les opérations
habituelles de messagerie : `PlatformRejected` expose les métadonnées `platformRejection`,
dont `authenticated` et, après corrélation, `outgoingEmailId`. Le statut n'est pas une preuve
SMTP ni une réponse humaine. Une entrée ambiguë reste visible sans restriction automatique.

Le MCP expose cette même structure dans `task.platformRejection`.

## Décider d'ignorer une alerte BODACC

Une tâche `Unknown` portant `extraData.idempotencyKey = bodacc-alert:<siren>:<noticeId>` concerne
une annonce précise. Son acquittement (`UnknownV1`, `value: true`) ferme la tâche et conserve la
pause BODACC. Le détail `GET /api/public/v1/accounts/:accountId` reste accessible après clôture et
porte `bodaccSiren`, `bodaccNoticeId`, `bodaccStatus`, les dates, le lien, `canIgnoreBodaccAlert`
et `bodaccAlertOverride` (état, justification, auteur, canal, dates).

Après une décision explicite de l'utilisateur, `POST
/api/public/v1/accounts/:accountId/bodacc-alert/ignore`, scope `dunning:manage`, accepte :

```json
{
  "siren": "123456789",
  "noticeId": "A202600011",
  "status": "Receivership",
  "confirmed": true,
  "justification": "Le document du débiteur confirme une autre identité."
}
```

Utiliser le triplet du constat réellement présenté, pas cet exemple synthétique. `taskId` est
facultatif et doit désigner une tâche de cette alerte. La justification est non vide après trim,
limitée à 2000 caractères. Le serveur tire l'auteur du contexte authentifié ; les champs inconnus
sont refusés. Une annonce devenue périmée produit `409`, relire la fiche avant une nouvelle décision.

La réponse porte `account`, `dunningStatus`, `blockingTasks`, `closedTaskIds`, `applied`,
`retryRequired` et `error`. Une décision persistée peut exiger le retry de la même requête après
un échec de reprise ou de clôture. Les autres pauses, attentes et tâches bloquantes restent
applicables. La décision ne certifie pas la légalité des relances et ne garantit aucun envoi
immédiat. Une entreprise `Dissolved` est dissoute, avec une créance présumée irrécouvrable.

`POST /api/public/v1/accounts/:accountId/bodacc-alert/revoke` accepte le même triplet et
`confirmed: true`, sans justification ni tâche, et rétablit le blocage selon le constat courant.
La surveillance continue et une nouvelle annonce invalide la décision précédente. Un ancien
contrôle sans `bodaccNoticeId` exige d'abord une nouvelle vérification. La correction durable du
SIREN est distincte : modifier la fiche d'un compte manuel ou la source d'un compte synchronisé.

### 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.

### Préparation, décision et résultat asynchrone

Un message texte peut demander une relance, désigner un contact et sélectionner une facture par
référence exacte ou échéance. L'agent prépare un brouillon à relire ; il ne l'arme pas. Sans fil,
un seul contact éligible est proposé, plusieurs contacts nécessitent un choix. Les factures ne
sont jointes que sur désignation explicite. Leurs pièces restent retirables et sont revérifiées
au moment de l'accord.

L'interaction structurée `DecideOutgoingEmailDraft` v1 reste l'unique décision d'accord ou
d'abandon. Après réception de l'interaction, relire la tâche et la communication jusqu'au reçu
métier. Un abandon confirmé retire le brouillon ; une absence signifie seulement qu'il n'est
plus disponible. Un résultat inconnu exige une vérification des communications avant une autre
action, et ne doit jamais provoquer un second envoi automatique. Les autres exigences de la tâche
et les pauses du compte ne sont pas annulées par cette décision.

### Sélection durable et tâches de routage

`ConfirmDunningRecipients` v1 répond aussi à `dunning-recipient-routing`. Une réponse texte reste
acceptée pour préciser un nom ou une nouvelle adresse ; une clôture vide ne confirme personne.
Les destinataires sélectionnés doivent déjà être actifs et porter une adresse valide dans une
confirmation structurée. `confirmLargeSelection` reste obligatoire au-delà de cinq contacts.

La lecture d'un compte expose `dunningRecipientSelection` avec `contacts`, `confirmedAt` et
`decision` (nullable, puis `taskId`, `interactionId`, `userId`, `decidedAt`). Un ancien choix peut
ne pas avoir de provenance. Le champ `excludedFromDunning` d'un contact vaut `selection` ou
`null`, jamais une permission d'écrire : seul le serveur détermine ce motif. Les outils MCP
existants exposent les mêmes données, sans nouvel outil. Un email ponctuel demandé dans une
tâche ne change pas automatiquement les destinataires de la cadence.

La sélection de destinataires expose aussi `redirection` lorsqu'un débiteur a explicitement
transmis le suivi à son successeur : identifiant et date de l'email, ancien et nouveau
destinataires. Cette preuve système ne se présente pas comme une décision d'un utilisateur.

Une réponse à une tâche peut préciser l'horaire de la prochaine relance **automatique**, avec
un fuseau IANA explicite, par exemple « prochaine relance automatique le 1 juillet 2027 à
08:00, Europe/Paris ». La balance expose `nextCommunicationReminderRequest`, sa date cible,
son fuseau et sa source. Le reçu confirme l'enregistrement de la consigne, pas la livraison.
Une instruction visant seulement un brouillon ponctuel n'utilise pas cette capacité.

### Factures concernées par une condition

Une tâche `NeedUserInput` peut exposer `conditionKind` et `affectedInvoices` dans ses
`extraData`. Ce dernier contient deux listes appariées : `invoiceIds` pour les identités
et `invoiceNumbers` pour l'affichage. Une valeur absente ou `null` conserve la portée du
compte ; une liste vide n'est pas une portée valide. Ces champs sont déterminés par le
système. Leur présence ne prouve pas, à elle seule, la pose effective des retenues : lire
également les détenteurs du `dunningBlock` des factures.

L'outil MCP `get-account-task` expose ces deux champs directement dans `task`.

---

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.
