> For the complete documentation index, see [llms.txt](https://boundaryai.gitbook.io/boundaryai-docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://boundaryai.gitbook.io/boundaryai-docs/boundaryai-docs-fr/api-et-webhooks/pushing-feedback.md).

# Envoi des retours

Envois unitaires, reprises en masse, import de fichiers, déduplication et anté-datation.

Tout sur l’ingestion des données, au-delà de la version en cinq minutes dans [Premiers pas](/boundaryai-docs/boundaryai-docs-fr/api-et-webhooks/getting-started.md): quel chemin d’ingestion utiliser, comment fonctionnent la déduplication et l’antidatage, et ce qui se passe après l’envoi.

***

### Trois façons d’ingérer

| Chemin                     | À utiliser quand                                                                                                                | Comportement                                                                                                                                                                               |
| -------------------------- | ------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `POST /feedback/push`      | Un flux régulier vers un seul champ : chaque ticket, avis ou appel au moment de sa clôture.                                     | Synchrone ; la réponse indique `inséré` et `skipped_duplicates`. Chaque élément devient un répondant distinct.                                                                             |
| `POST /feedback/push/bulk` | Plusieurs champs en un seul appel : un enregistrement complet par répondant, des reprises historiques, des traitements par lot. | Asynchrone **202** avec une `task_id` et `status_url` par défaut ; interrogez jusqu’à `terminé`. Les petits lots peuvent s’exécuter de manière synchrone avec `"sync": true` (ci-dessous). |
| `POST /feedback/upload`    | Vous disposez déjà d’un export CSV ou XLSX.                                                                                     | Téléversement multipart ; même flux de tâche asynchrone que le mode bulk.                                                                                                                  |

Les trois arrivent au même endroit et déclenchent la même analyse. Préférez le bulk aux envois unitaires en boucle : un seul appel pour des milliers d’éléments est plus respectueux de vos limites de débit (60/minute par clé par défaut) et de la plateforme.

#### Envoi bulk : un répondant, plusieurs champs

`/feedback/push/bulk` prend une liste de `champs`, chacun avec son propre `content` array, et enregistre tout le lot comme les réponses d’un seul répondant. C’est ainsi que vous envoyez un enregistrement complet (le commentaire ouvert *et* et son score NPS) sous forme d’une seule ligne plutôt que de lignes sans rapport ; à l’inverse, le point de terminaison d’envoi unitaire traite chaque élément comme un répondant distinct. Lorsque chaque enregistrement correspond à une personne différente, envoyez un enregistrement par appel bulk (le mode synchrone ci-dessous est conçu exactement pour cela). Un `source_reference` (jusqu’à 255 caractères) est stocké sur chaque ligne du lot, afin que vous puissiez identifier le système ou le traitement qui l’a produit.

#### Envoi bulk synchrone

Les plateformes d’intégration et les récepteurs de webhook livrent généralement un enregistrement par appel et ne peuvent pas interroger un identifiant de tâche. Ajoutez `"sync": true` et le lot s’exécute en ligne, en renvoyant **200** avec le résultat final par champ au lieu d’un identifiant de tâche :

```bash
curl -X POST https://boundaryai-ingest-279197672085.europe-west9.run.app/api/input/feedback/push/bulk \
  -H "Authorization: Bearer $BAI_API_KEY" -H "Content-Type: application/json" \\
  -H "Idempotency-Key: ticket-58121-v1" \
  -d '{
    "feedback_group_id": "1842",
    "source_id": "9021",
    "sync": true,
    "source_reference": "crm-webhook",
    "fields": [
      {"field_id": "31245", "content": [{"text": "Le support a résolu mon problème en un seul appel.", "external_id": "ticket-58121"}]},
      {"field_id": "31246", "content": [{"text": "9", "external_id": "ticket-58121"}]}
    ]
  }'
```

Les règles qui garantissent la sécurité :

* **Chaque ligne doit comporter un `external_id`**; sinon, l’appel est rejeté avec `SYNC_REQUIRES_EXTERNAL_ID` (400). Un envoi en ligne peut être interrompu par son délai d’expiration, et l’identifiant est ce qui permet à une nouvelle tentative de converger vers les mêmes lignes au lieu de les dupliquer.
* **Petits lots uniquement**: jusqu’à 50 éléments répartis sur 20 champs maximum. Les lots plus volumineux, et les requêtes arrivant pendant que la capacité en ligne est occupée, basculent silencieusement vers le chemin asynchrone et renvoient **202** avec un identifiant de tâche, de sorte qu’un client qui envoie toujours `synchronisation` fonctionne toujours.
* **Codes de statut transparents**: si aucun champ n’a accepté quoi que ce soit, vous obtenez **400** `PUSH_FAILED` avec le détail par champ plutôt qu’un 200 qui masque les lignes rejetées. Un envoi qui dépasse son budget de 60 secondes renvoie **504** `SYNC_PUSH_TIMEOUT`; les lignes déjà écrites restent écrites, alors réessayez avec les mêmes `external_id` valeurs.

***

### Éléments structurés : les champs qui méritent leur place

Un élément dans `content` peut être une simple chaîne, mais les intégrations de production devraient envoyer des objets :

```json
{"text": "L'assistance a résolu mon problème en un seul appel.",
 "external_id": "ticket-58121",
 "customer_id": "cus_310",
 "channel": "support",
 "language": "en",
 "rating": 5,
 "occurred_at": "2026-07-12T09:30:00Z"}
```

* **`external_id`** est votre identifiant stable, et c’est ce qui rend les nouvelles tentatives sûres : dans un champ, un second envoi avec un `external_id` qui existe déjà est **ignoré**, et la réponse le comptabilise sous `skipped_duplicates` avec les identifiants dans `duplicate_external_ids`. Notez qu’il s’agit d’un saut, pas d’une mise à jour : envoyer un texte corrigé sous le même `external_id` laisse l’original en place. Sans `external_id`, un lot rejoué signifie des commentaires en double qui polluent votre analyse.
* **`occurred_at`** antidate l’élément à la date à laquelle le retour s’est réellement produit. Cela est particulièrement important pour [les groupes suivis dans le temps](/boundaryai-docs/boundaryai-docs-fr/analyser-vos-retours/tracking-feedback-over-time-evolution.md): un ticket de juin envoyé en juillet tombe dans la période de juin, pas de juillet. Si elle est omise, la date des éléments correspond à leur arrivée.
* **`customer_id`** lie les éléments à une personne à travers les sources, alimente [l’effacement en masse](#erasing-and-listing-what-you-pushed), et s’associe avec le `external_id` sur [invitations](/boundaryai-docs/boundaryai-docs-fr/api-et-webhooks/sending-invites.md) afin que vous puissiez joindre l’ensemble de la boucle dans votre entrepôt.
* **`canal`**, **`langue`**, **`note`** deviennent des métadonnées de segmentation, exactement comme les colonnes de métadonnées d’un [téléversement](/boundaryai-docs/boundaryai-docs-fr/importer-vos-retours/uploading-an-existing-dataset.md).

Les champs numériques (NPS, notes) prennent leur valeur à partir du `texte` (`"9"`); les valeurs non finies sont rejetées à l’ingestion.

Deux niveaux de sécurité pour les nouvelles tentatives se combinent : `external_id` déduplique au niveau de l’élément pour toujours, et le **`Idempotency-Key`** l’en-tête rend toute requête rejouable pendant 24 heures (même clé = même réponse, aucun retraitement). Utilisez les deux ; les réseaux tombent en panne en plein milieu d’une requête plus souvent que quiconque ne le souhaite.

***

### Ce qui se passe après un envoi

1. Les éléments sont nettoyés, la langue est détectée et ils sont automatiquement mis en file pour analyse ; il n’y a pas d’appel « run analysis » à effectuer.
2. Les moniteurs de groupe avec **Couvrir automatiquement les nouvelles sources** surveillent déjà les sources créées par l’API ([Surveillance personnalisée](/boundaryai-docs/boundaryai-docs-fr/analyser-vos-retours/custom-monitoring.md)),
3. Lorsque le passage est terminé, le **`analysis.completed`** webhook se déclenche, et `GET /sources/{id}/analysis` renvoie la répartition des sentiments, les thèmes et les correspondances des moniteurs. L’analyse prend de quelques minutes à plusieurs dizaines de minutes selon le volume ; tant qu’une exécution n’est pas terminée, la lecture renvoie `analysis_status: "none"` avec des chiffres à zéro qui ne constituent pas des résultats.
4. Pour une source dans un groupe qui suit les retours dans le temps, la lecture agrège chaque période analysée (le même corpus que la vue Globale du tableau de bord) et renvoie `analysis_status: "available"` avec `analysis_id: null`; basez-vous sur `analysis_status`, jamais sur `analysis_id`.
5. Les données participent à tout le reste : thèmes groupés, périodes d’évolution, rapports et tableaux de bord, sans distinction avec les données d’enquête ou de téléversement.

L’envoi consomme le **quota d’utilisation** lorsqu’il est analysé, selon le même comptage que toute autre analyse sur la plateforme ; voir [Paramètres](/boundaryai-docs/boundaryai-docs-fr/compte-et-administration/updating-settings.md#usage). `rows_accepted` est le nombre de lignes admises (`aps_deducted` est son alias hérité) ; rien n’est facturé au moment de l’envoi, et les clés de test ne sont jamais facturées.

***

### Effacer et lister ce que vous avez envoyé

Vous gardez le contrôle des données créées par votre intégration :

* `GET /feedback` liste les éléments envoyés par l’API (filtrez par `source_id` / `field_id`, paginée par curseur), utile pour les tâches de rapprochement. N’inclut jamais les réponses natives aux enquêtes.
* `POST /feedback/erase` supprime en masse par `external_ids` ou par `customer_id`, ce qui constitue la partie mécanique du respect d’une demande d’effacement RGPD : un seul appel supprime le feedback envoyé par API d’une personne à travers les sources. Cela ne touche que les lignes envoyées par API ; les réponses aux enquêtes et les téléversements disposent de leurs propres flux de suppression côté produit.

***

### Liste de vérification d’intégration

* Une clé par intégration, `push` portée, `Idempotency-Key` sur chaque écriture.
* Envoyez toujours `external_id` et `occurred_at`; votre futur vous, lors d’une resynchronisation, vous remerciera.
* Utilisez le mode bulk pour tout ce qui dépasse une poignée d’éléments ; interrogez le `status_url` ou abonnez-vous à `content.pushed`. Utilisez `sync: true` lorsque votre appelant a besoin du résultat dans la même réponse.
* Abonnez-vous à `analysis.completed` au lieu d’interroger le point de terminaison d’analyse.
* Associez `customer_id` à l’identifiant que votre CRM utilise, afin que l’effacement et les jointures d’invitations restent des opérations en un seul appel.
