---
page: keywords
sous-page: export-import
route: /keywords/export-import
statut: draft
version: 0.1
transverse-liées: [reservoir-data-model (TBD), sources, queue-workers (TBD), auth (TBD), conventions, i18n, design-system]
date: 2026-06-03
---

# CdC — `keywords/export-import`

> Le **moteur de miscibilité** du Reservoir : faire entrer/sortir des keywords depuis les 15 sources de [`_transverse/sources.md`](../_transverse/sources.md), via **fichiers (CSV/XLSX)** *et* **API**, à travers un **même pipeline** (mapping → normalisation → dédup → `raw` conservé).
>
> **v1 = fichiers**. Les **connecteurs API sont spécifiés** ici et branchés sur la même abstraction « connecteur » ; leur implémentation est **phasée** (planifiée, pas v1).

## 1. Objectif

Alimenter le Reservoir (import) et en sortir des données (export) de façon **miscible** : quelle que soit la source (Mangools, Ahrefs, Google…), tout converge vers le **modèle canonique**, avec **métriques taguées par source**, **série mensuelle** et **`raw` conservé** pour ré-export fidèle. Inclut la **boucle de re-check** (exporter vers Google, ré-importer le retour).

## 2. Périmètre

- **Dans** : import **fichier** (CSV/XLSX) + **abstraction connecteur API** (spécifiée, phasée) ; **mapping profiles** (presets 15 sources + custom) ; **auto-détection** de la source ; **preview/diff** avant commit ; **dédup/merge** ; **série mensuelle** ; **`raw`** + **provenance** ; **export** multi-format ; **boucle de re-check** ; **jobs asynchrones** (queue/workers) ; **dry-run/validation**.
- **Hors** : modèle de données du Reservoir (→ `reservoir`/transverse). Découverte de keywords sur le web (→ pas dans le périmètre keywords). Implémentation concrète des connecteurs API (→ phase ultérieure, mais **contrat défini ici**).

## 3. User stories *(persona : consultant SEO)*

- J'uploade un **export Mangools** : la source est **reconnue**, le mapping pré-rempli, je vois un **aperçu (créé / fusionné / conflit)** avant de valider.
- Je ré-importe la même source plus tard : ça **ajoute un snapshot**, sans **dupliquer** le keyword.
- J'**exporte mes keywords *watch* au format Google Keyword Planner**, je ré-importe le retour, et je vois l'**évolution**.
- Je crée un **mapping custom** pour un CSV d'un outil non encore connu, et je le **réutilise**.
- (Plus tard) je connecte une **API** (Google/Ahrefs/…) et je **planifie un import périodique** — même pipeline, même résultat.

## 4. Fonctionnalités

**Import**
- F1 — **Sélecteur de site global** ; import rattaché à **un site + une langue** (défaut au lot, **surchargeable par colonne** si la source porte la locale).
- F2 — Upload **CSV/XLSX** (et, phasé, **pull API**) → **job d'import**. Après **auto-détection de la source** (R1, `_transverse/conventions.md`), l'utilisateur **choisit la destination** (Reservoir par défaut, ou `planning`, …) — **pas figée**.
- F3 — **Mapping profiles** : presets pour les 15 sources + **mapping custom**. Support spécial du **bloc série temporelle** (colonnes `Search Volume M/YYYY` → `keyword_volume_point`).
- F4 ⭐ — **Auto-détection de la source** par **signature d'en-tête** → mapping pré-rempli.
- F5 ⭐ — **Preview + diff** : compte de **créés / fusionnés / en conflit**, échantillon, avant **commit**.
- F6 — **Dédup/merge** sur `(site, langue, terme_norm)` : ajoute un **snapshot tagué source**, **jamais d'écrasement** ; `raw` conservé.
- F7 — **Provenance/lignée** : chaque snapshot garde (source, fichier/connecteur, lot, date).
- F8 — **Dry-run/validation** : lignes malformées, doublons intra-fichier, colonnes inconnues, **succès partiel**.

**Export**
- F9 — Export d'une **sélection** ou d'une **vue sauvegardée** → formats : **canonique CSV**, **fidèle-source** (depuis `raw`), **Google Keyword Planner**.
- F10 ⭐ — **Round-trip de re-check** : export *watch* → Google → ré-import → **diff des métriques dans le temps**.

**Connecteurs & exécution**
- F11 — **Abstraction connecteur** : un connecteur = `file` **ou** `api`, déclarant ses capacités (`import`/`export`) ; même pipeline aval.
- F12 — (Phasé) **Imports planifiés** pour les sources API (rafraîchissement périodique).
- F13 — **Jobs asynchrones** : statut, progression, erreurs, reprise.

## 5. UI / écrans

- **Assistant d'import** : 1) source/upload → 2) **mapping** (auto-détecté, éditable) → 3) **preview/diff** → 4) **commit**.
- **Liste des jobs** (import/export) : statut, stats (créés/fusionnés/conflits/erreurs), téléchargement, relance.
- **Éditeur de mapping profile** (réutilisable).
- **Assistant d'export** : sélection/vue → format cible → job → download.
- États vide/chargement/erreur ; responsive. Tokens `design.md` (formulaires, `comparison-table` pour le diff).

## 6. Données & actions

- **Affiché** : assistant d'import (mapping + diff), jobs, mappings, assistant d'export.
- **Actions** : uploader/puller, mapper, prévisualiser, committer, exporter, créer/réutiliser un mapping, planifier (API), relancer un job.

## 7. Modèle de données *(ajout au socle — détail dans `_transverse/reservoir-data-model.md`, TBD)*

```
connector(source, type, caps)            -- type ∈ {file, api} ; caps ∈ {import, export}
mapping_profile(id, source, owner, header_signature?, fields_map JSON, series_rule?)
                                          -- owner ∈ {builtin, custom} ; fields_map: colonne source → champ canonique
import_job(id, site_id, source, connector_type, language_default,
           status, stats JSON, file_ref?, created, finished)
export_job(id, site_id, target_format, selection_ref?, view_id?,
           status, file_ref?, created, finished)
```

- Rappel socle : `keyword`, `keyword_source_metric(... source, raw, captured_at)`, `keyword_volume_point(... source, month, volume)`.
- **Lignée** : ajouter `import_job_id` sur `keyword_source_metric`.
- **Idempotence** : ré-importer la même source = **nouveau snapshot** (`captured_at`), pas de doublon de `keyword`.
- **À trancher** : stockage des **secrets API** (→ `_transverse/auth`), versionnage des `mapping_profile`.

## 8. API Hono

- `GET /connectors` *(liste + capacités file/api)*
- `GET|POST /mapping-profiles` · `PATCH /mapping-profiles/:id`
- `POST /sites/:siteId/imports` *(upload fichier **ou** déclenche un pull API)* → job
- `GET /sites/:siteId/imports/:jobId` · `GET …/preview` · `POST …/commit`
- `POST /sites/:siteId/imports/schedule` *(sources API — phasé)*
- `POST /sites/:siteId/exports` *(format cible, sélection/vue)* → job
- `GET /sites/:siteId/exports/:jobId` *(statut + download)*

> Routes **préfixées par `siteId`**. Imports/exports = **jobs** (queue/workers, voir `_transverse/queue-workers`).

## 9. Capacités requises *(fonctionnel — mapping modules TBD)*

Ingestion **multi-format (fichier + API)** · mapping/normalisation (dont **bloc série temporelle**) · **auto-détection** de source · dédup/merge idempotent · **jobs asynchrones** (file d'attente) · validation/dry-run · export multi-format · **planification** (API) · gestion de **secrets** (API).

## 10. Tools / capacités MCP

- `import_start` (write) · `import_preview` (read) · `import_commit` (write) · `import_status` (read)
- `export_start` (write) · `export_status` (read)
- `connectors_list` (read) · `mapping_profile_get` (read) · `mapping_profile_set` (write)
- `recheck_roundtrip` (write)

> Privé vs public-limité : **TBD**.

## 11. i18n

- UI localisée (clés). La **langue d'import** est une **dimension data** (rattachement du lot), distincte de l'UI.
- Sources avec **`Location`/pays** → renseigne `keyword_source_metric.location` (utile pour `recherche`). Réf `_transverse/i18n.md` (TBD).

## 12. Qualité *(réf. fiches des 128 par slug)*

- **Sécu** : **validation des fichiers** (type/taille/sanitisation), `x-content-type-options`, `cookie-attributes`, auth ; **secrets API** chiffrés ; isolation **par site**.
- **Perf** : fichiers volumineux **streamés**, traitement en **jobs** ; `cache-control`.
- **A11y** : assistant multi-étapes **opérable clavier**, `form-labels`, `form-errors`, `focus-indicators`.
- **Indexation** : app privée → `meta-robots` (noindex).

## 13. Critères d'acceptation

- [ ] Un **export Mangools** uploadé est **auto-détecté**, mapping pré-rempli, **bloc série mensuelle reconnu**.
- [ ] La **preview** annonce correctement créés / fusionnés / conflits avant commit.
- [ ] Le commit crée `keyword` + `keyword_source_metric` (+ `raw`) + `keyword_volume_point`.
- [ ] **Ré-importer** la même source ajoute un **snapshot**, sans dupliquer le keyword.
- [ ] **Ré-exporter** reproduit fidèlement les données d'origine (`raw`).
- [ ] L'export **Google Keyword Planner** produit un fichier **ré-importable** (round-trip).
- [ ] Un **connecteur API** (même stub) suit **le même pipeline** que le fichier.
- [ ] Le **dry-run** signale lignes malformées / doublons intra-fichier / colonnes inconnues.
- [ ] Le **switch de site** isole tout.

## 14. Dépendances & risques · questions ouvertes

- **Dépend de** : `_transverse/sources` (mapping profiles), `_transverse/reservoir-data-model`, `_transverse/queue-workers` (jobs), `_transverse/auth` (secrets API), `keywords/reservoir` (cible).
- **Risques** : hétérogénéité réelle des exports (colonnes variables d'une même source dans le temps) ; **rate limits** API ; volumétrie des séries mensuelles ; conflits de fusion.
- **Questions ouvertes** :
  - Quelles sources ont une **API officielle** exploitable (vs CSV-only) ?
  - **Granularité de planification** des imports API ?
  - Versionnage des `mapping_profile` quand une source change son format ?
  - Stockage/chiffrement des **secrets API** (→ `_transverse/auth`).
