---
title: API REST – Import en masse (upsert)
description: "Cette page décrit l'opération à utiliser pour **charger ou mettre à jour beaucoup d'enregistrements d'un coup**, en les désignant par une **clef métier** (un code article, une référence client, une référence boutique) plutôt que par leur identifiant Kafinea. | Opération…"
url: "https://www.kafinea.com/fr/documentation/api/api-rest-import-en-masse/"
updated: "2026-11-04"
category: Intégrations / API
---

# API REST – Import en masse (upsert)

Cette page décrit l'opération à utiliser pour **charger ou mettre à jour beaucoup d'enregistrements
 d'un coup**, en les désignant par une **clef métier** (un code article, une référence client, une
 référence boutique) plutôt que par leur identifiant Kafinea.

| Opération | Méthode | Description |
| --- | --- | --- |
| `mass_upsert` | POST | Écrire un lot d'enregistrements identifiés par une clef métier |

Elle est **générique** : elle fonctionne pour tous les modules, avec la clef que vous choisissez.

> **Réservée aux administrateurs.** Charger un lot d'enregistrements est un travail de reprise de
>  données. Un compte non administrateur garde `create` et `update`, un enregistrement à la fois, avec
>  ses propres droits.

---

## Pourquoi pas `create` ou `update` ?

Les opérations [`create` et `update`](https://www.kafinea.com/fr/documentation/api/api-rest-operations-crud/) restent la bonne réponse quand vous
 écrivez un enregistrement à la fois. Pour une reprise de données, elles butent sur deux points :

- **le volume** : un enregistrement par appel, soit 16 000 appels pour 16 000 clients ;
- **la reprise après interruption** : `create` crée toujours. Un import coupé au milieu ne peut pas
   être relancé sans nettoyer d'abord ce qui est déjà passé, et `update` demande de connaître
   l'identifiant Kafinea, que vous n'avez justement pas encore.

`mass_upsert` retrouve l'enregistrement par sa clef métier : il met à jour ce qui existe, crée ce qui
 manque, et **n'écrit rien** quand les valeurs envoyées sont déjà celles en place. Vous pouvez donc
 relancer le même lot autant de fois que nécessaire.

---

## Upsert – Écrire un lot d'enregistrements

> **POST** `https://apps.kafinea.com/{instance}/webservice.php`

| Paramètre | Type | Requis | Description |
| --- | --- | --- | --- |
| `operation` | string | oui | Doit être `mass_upsert` |
| `sessionName` | string | oui | Identifiant de session |
| `elementType` | string | oui | Nom du module (ex : `Products`, `Accounts`) |
| `keyFields` | JSON | oui | Noms techniques des champs formant la clef métier |
| `elements` | JSON | oui | Tableau des enregistrements (500 au maximum par appel) |

Chaque élément de `elements` est un objet `champ : valeur`, **clef comprise**. Les noms techniques des
 champs se lisent avec [`describe`](https://www.kafinea.com/fr/documentation/api/api-rest-champs-de-module/).

**Exemple curl :** mettre à jour le poids et le pays d'origine d'articles reconnus par leur code.

```bash
curl -X POST https://apps.kafinea.com/YourKafinea/webservice.php 
  -d "operation=mass_upsert" 
  -d "sessionName=YOUR_SESSION_ID" 
  -d "elementType=Products" 
  -d 'keyFields=["productcode"]' 
  -d 'elements=[
        {"productcode":"9782070413110","productname":"Le Petit Prince","mdsweight":"0.180","mdsorigincountry":"FRA"},
        {"productcode":"9782070368228","productname":"La Peste","mdsweight":"0.150","mdsorigincountry":"FRA"}
      ]'
```

### Réponse

```json
{
  "success": true,
  "result": {
    "module": "Products",
    "keyFields": ["productcode"],
    "processed": 2,
    "created": 1,
    "updated": 1,
    "unchanged": 0,
    "records": [
      { "index": 0, "id": "14x8842", "outcome": "updated" },
      { "index": 1, "id": "14x9317", "outcome": "created" }
    ],
    "errors": []
  }
}
```

| Champ | Signification |
| --- | --- |
| `processed` | Nombre d'enregistrements traités sans erreur |
| `created` | La clef était inconnue : l'enregistrement vient d'être créé |
| `updated` | La clef existait et au moins une valeur envoyée était différente |
| `unchanged` | La clef existait et tout était déjà à jour : rien n'a été écrit |
| `records` | Le détail ligne par ligne, avec la **position dans le lot** et l'identifiant obtenu |
| `errors` | Les lignes refusées, avec leur position dans le lot et le motif |

---

## Choisir la clef métier

La clef est celle que vous nommez dans `keyFields`, et elle peut compter plusieurs champs. Elle doit
 désigner **un seul** enregistrement : si la clef envoyée en retrouve plusieurs, la ligne est refusée
 avec ce motif plutôt que d'en choisir un au hasard. Corrigez la donnée en double, puis renvoyez la
 ligne.

Quelques clefs usuelles :

| Ce que vous importez | Clef métier |
| --- | --- |
| Des articles | `["productcode"]` |
| Des correspondances d'identifiants externes | `["sourcesystem","objecttype","externalref"]` |

Les valeurs de clef sont comparées après suppression des espaces de début et de fin. Une valeur de
 clef vide fait refuser la ligne : une clef métier ne se devine pas.

---

## Rejouable : relancez sans crainte

L'opération est **idempotente**. Le même lot envoyé deux fois donne `unchanged` au second passage :
 ni doublon, ni date de modification inutilement remuée, ni ligne d'historique parasite sur les fiches.

De même, **une ligne fautive n'annule pas les autres** : les lignes valides du lot sont écrites, les
 lignes refusées reviennent dans `errors` avec leur position, à corriger puis à renvoyer seules.

En revanche, si le paramètre `elements` lui-même n'est pas lisible (JSON invalide, plus de 500
 éléments), l'appel entier est refusé : écrire la moitié d'un lot incompréhensible serait pire que de
 n'en écrire aucune partie.

Enfin, l'opération n'ouvre aucune porte dérobée : elle applique les mêmes droits et les mêmes
 contrôles de champs obligatoires que `create` et `update`. Ce que ces opérations refusent, celle-ci le
 refuse aussi.

---

## Cas d'usage : déclarer les identifiants d'un système extérieur

Le besoin le plus fréquent est de dire à Kafinea **quel enregistrement correspond à quel objet d'un
 système extérieur** : un client de votre boutique en ligne, un article de votre place de marché, une
 fiche issue de votre ancien logiciel.

C'est la table de correspondance que Kafinea consulte **avant** toute tentative de rapprochement par
 nom ou par e-mail. Une correspondance déclarée est une **certitude**, pas une supposition : elle
 supprime les doublons de fiches clients à la source. Une correspondance porte trois informations :

| Champ | Rôle |
| --- | --- |
| `sourcesystem` | D'où vient l'identifiant. Pour une boutique en ligne : `onlinestore:` suivi de l'identifiant de la fiche boutique |
| `objecttype` | Le module Kafinea concerné (`Accounts`, `Products`, `SalesOrder`…) |
| `externalref` | La valeur que le système extérieur donne à cet objet |
| `recordid` | L'enregistrement Kafinea visé, sous la forme d'un identifiant Webservice (`10x54325`) |

**L'unicité est garantie par système source, et c'est le point important.** Un même client peut
 porter un identifiant différent sur chacune de vos boutiques, et deux boutiques peuvent utiliser le
 même numéro pour deux clients différents : la correspondance sait l'exprimer, un simple champ « réf.
 externe » sur la fiche client ne le saurait pas.

```bash
curl -X POST https://apps.kafinea.com/YourKafinea/webservice.php 
  -d "operation=mass_upsert" 
  -d "sessionName=YOUR_SESSION_ID" 
  -d "elementType=MdsExternalRef" 
  -d 'keyFields=["sourcesystem","objecttype","externalref"]' 
  -d 'elements=[
        {"sourcesystem":"onlinestore:44053","objecttype":"Accounts","externalref":"39146","recordid":"10x54325"},
        {"sourcesystem":"onlinestore:44053","objecttype":"Products","externalref":"3760174130121","recordid":"13x8842"}
      ]'
```

`recordid` désigne un enregistrement : il s'écrit et se relit comme tout lien vers un enregistrement
 dans l'API, avec l'**identifiant Webservice complet** que renvoient `query` ou `retrieve`. Un
 identifiant brut (`54325`) est refusé. L'enregistrement visé est ainsi un lien cliquable dans
 Kafinea, et la fiche client affiche la liste de ses identifiants externes.

Les correspondances déclarées se relisent comme n'importe quel autre module, avec
 [`query`](https://www.kafinea.com/fr/documentation/api/api-rest-requetes-et-interrogation/).

### Reprendre une base clients existante

Le scénario typique est la mise en service d'une boutique en ligne dont les clients existent déjà
 dans Kafinea. Sans correspondances, la synchronisation ne peut que deviner, et crée des doublons.

1. Réglez, sur la fiche de la boutique, la **reconnaissance du client** sur « Réf. client boutique »
    (voir le paramétrage de la boutique en ligne).
2. Déclarez les correspondances par lots de 500, avec cette opération.
3. Lancez la synchronisation : chaque commande retrouve son client par identité.

Un client absent de la table n'est **pas** créé en silence si vous avez décoché « Créer les clients
 depuis la boutique » : la commande est rattachée au client de repli de la boutique et l'anomalie est
 nommée dans le journal de synchronisation. Vous complétez la correspondance, puis vous rafraîchissez
 la commande depuis la boutique.

---

## Références associées 🔗

- [Opérations CRUD](https://www.kafinea.com/fr/documentation/api/api-rest-operations-crud/)
- [Champs d'un module](https://www.kafinea.com/fr/documentation/api/api-rest-champs-de-module/)
- [Suppression en masse (corbeille)](https://www.kafinea.com/fr/documentation/api/api-rest-suppression-en-masse/)
- [Requêtes et interrogation](https://www.kafinea.com/fr/documentation/api/api-rest-requetes-et-interrogation/)
- [Introduction API](https://www.kafinea.com/fr/documentation/api/api-rest-introduction/)
