diff --git a/backend/src/main/java/fr/inra/urgi/faidare/repository/es/GermplasmRepository.java b/backend/src/main/java/fr/inra/urgi/faidare/repository/es/GermplasmRepository.java
index 7c12185204b63e1ed9a3ccd6e6c9da7092a91dca..8fbd47d1273fcd733a96d6c0543bc5b1d6a133d1 100644
--- a/backend/src/main/java/fr/inra/urgi/faidare/repository/es/GermplasmRepository.java
+++ b/backend/src/main/java/fr/inra/urgi/faidare/repository/es/GermplasmRepository.java
@@ -58,6 +58,11 @@ public interface GermplasmRepository
      */
     Iterator<GermplasmMcpdVO> scrollGermplasmMcpdsByIds(Set<String> ids, int fetchSize);
 
+    /**
+     * Scroll through all germplasm having one of the given IDs.
+     */
+    Iterator<GermplasmVO> scrollGermplasmsByIds(Set<String> ids, int fetchSize);
+
     /**
      * Find pedigree for germplasm by id.
      */
diff --git a/backend/src/main/java/fr/inra/urgi/faidare/repository/es/GermplasmRepositoryImpl.java b/backend/src/main/java/fr/inra/urgi/faidare/repository/es/GermplasmRepositoryImpl.java
index 616a33d7e6083e9f1abc027f590b692a9a17573e..d5af326a0bb8df2f51206c0408ac2c6cc060255c 100644
--- a/backend/src/main/java/fr/inra/urgi/faidare/repository/es/GermplasmRepositoryImpl.java
+++ b/backend/src/main/java/fr/inra/urgi/faidare/repository/es/GermplasmRepositoryImpl.java
@@ -125,6 +125,13 @@ public class GermplasmRepositoryImpl implements GermplasmRepository {
         return new ESScrollIterator<>(client, requestFactory, parser, GermplasmMcpdVO.class, query, fetchSize);
     }
 
+    @Override
+    public Iterator<GermplasmVO> scrollGermplasmsByIds(Set<String> ids,
+                                                       int fetchSize) {
+        QueryBuilder query = QueryBuilders.termsQuery("germplasmDbId", ids);
+        return new ESScrollIterator<>(client, requestFactory, parser, GermplasmVO.class, query, fetchSize);
+    }
+
     @Override
     public GermplasmVO getById(String germplasmDbId) {
         return getByIdRepository.getById(germplasmDbId);
diff --git a/backend/src/main/java/fr/inra/urgi/faidare/web/germplasm/GermplasmController.java b/backend/src/main/java/fr/inra/urgi/faidare/web/germplasm/GermplasmController.java
index bfe7d484cb1b5afaed7e17f239db0c29ec5e8153..9cf89be05915532b1bc418c32108f865dfd330ac 100644
--- a/backend/src/main/java/fr/inra/urgi/faidare/web/germplasm/GermplasmController.java
+++ b/backend/src/main/java/fr/inra/urgi/faidare/web/germplasm/GermplasmController.java
@@ -49,17 +49,20 @@ public class GermplasmController {
     private final FaidareProperties faidareProperties;
     private final XRefDocumentRepository xRefDocumentRepository;
     private final GermplasmAttributeRepository germplasmAttributeRepository;
+    private final GermplasmMcpdExportService germplasmMcpdExportService;
     private final GermplasmExportService germplasmExportService;
 
     public GermplasmController(GermplasmRepository germplasmRepository,
                                FaidareProperties faidareProperties,
                                XRefDocumentRepository xRefDocumentRepository,
                                GermplasmAttributeRepository germplasmAttributeRepository,
+                               GermplasmMcpdExportService germplasmMcpdExportService,
                                GermplasmExportService germplasmExportService) {
         this.germplasmRepository = germplasmRepository;
         this.faidareProperties = faidareProperties;
         this.xRefDocumentRepository = xRefDocumentRepository;
         this.germplasmAttributeRepository = germplasmAttributeRepository;
+        this.germplasmMcpdExportService = germplasmMcpdExportService;
         this.germplasmExportService = germplasmExportService;
     }
 
@@ -86,13 +89,25 @@ public class GermplasmController {
         return toModelAndView(germplasms.get(0));
     }
 
-    @PostMapping("/exports")
+    @PostMapping("/exports/mcpd")
+    @ResponseBody
+    public ResponseEntity<StreamingResponseBody> export(@Validated @RequestBody GermplasmMcpdExportCommand command) {
+        List<GermplasmMcpdExportableField> fields = getFieldsToExport(command);
+
+        StreamingResponseBody body = out -> {
+            Iterator<GermplasmMcpdVO> iterator = germplasmRepository.scrollGermplasmMcpdsByIds(command.getIds(), 1000);
+            germplasmMcpdExportService.export(out, iterator, fields);
+        };
+        return ResponseEntity.ok().contentType(MediaType.parseMediaType("text/csv")).body(body);
+    }
+
+    @PostMapping("/exports/plant-material")
     @ResponseBody
     public ResponseEntity<StreamingResponseBody> export(@Validated @RequestBody GermplasmExportCommand command) {
         List<GermplasmExportableField> fields = getFieldsToExport(command);
 
         StreamingResponseBody body = out -> {
-            Iterator<GermplasmMcpdVO> iterator = germplasmRepository.scrollGermplasmMcpdsByIds(command.getIds(), 1000);
+            Iterator<GermplasmVO> iterator = germplasmRepository.scrollGermplasmsByIds(command.getIds(), 1000);
             germplasmExportService.export(out, iterator, fields);
         };
         return ResponseEntity.ok().contentType(MediaType.parseMediaType("text/csv")).body(body);
@@ -192,7 +207,17 @@ public class GermplasmController {
         return germplasmRepository.findPedigree(germplasm.getGermplasmDbId());
     }
 
-    private List<GermplasmExportableField> getFieldsToExport(GermplasmExportCommand command) {
+    private List<GermplasmMcpdExportableField> getFieldsToExport(
+        GermplasmMcpdExportCommand command) {
+        List<GermplasmMcpdExportableField> fields = command.getFields();
+        if (fields.isEmpty()) {
+            fields = Arrays.asList(GermplasmMcpdExportableField.values());
+        }
+        return fields;
+    }
+
+    private List<GermplasmExportableField> getFieldsToExport(
+        GermplasmExportCommand command) {
         List<GermplasmExportableField> fields = command.getFields();
         if (fields.isEmpty()) {
             fields = Arrays.asList(GermplasmExportableField.values());
diff --git a/backend/src/main/java/fr/inra/urgi/faidare/web/germplasm/GermplasmExportService.java b/backend/src/main/java/fr/inra/urgi/faidare/web/germplasm/GermplasmExportService.java
index dc8289ffdeff97c140b4f298ed62f0ae03dc2a82..b17538a92f5edf80493c0dcdf6374a3118030e95 100644
--- a/backend/src/main/java/fr/inra/urgi/faidare/web/germplasm/GermplasmExportService.java
+++ b/backend/src/main/java/fr/inra/urgi/faidare/web/germplasm/GermplasmExportService.java
@@ -17,9 +17,9 @@ import java.util.function.Function;
 import java.util.stream.Collectors;
 
 import com.opencsv.CSVWriter;
-import fr.inra.urgi.faidare.domain.data.germplasm.DonorInfoVO;
+import fr.inra.urgi.faidare.domain.data.germplasm.CollPopVO;
 import fr.inra.urgi.faidare.domain.data.germplasm.GermplasmMcpdVO;
-import fr.inra.urgi.faidare.domain.data.germplasm.InstituteVO;
+import fr.inra.urgi.faidare.domain.data.germplasm.GermplasmVO;
 import org.springframework.stereotype.Component;
 
 /**
@@ -34,85 +34,19 @@ public class GermplasmExportService {
     public GermplasmExportService() {
         Map<GermplasmExportableField, GermplasmExportableFieldDescriptor> map = new HashMap<>();
 
-        map.put(PUID, withFieldAsHeader(PUID, vo -> vo.getGermplasmPUI()));
-        map.put(INSTCODE, withFieldAsHeader(INSTCODE, vo -> vo.getInstituteCode()));
-        map.put(ACCENUMB, withFieldAsHeader(ACCENUMB, vo -> vo.getAccessionNumber()));
-        map.put(COLLNUMB, withFieldAsHeader(COLLNUMB, vo -> vo.getCollectingInfo().getCollectingNumber()));
-        map.put(COLLCODE, withFieldAsHeader(COLLCODE, vo ->
-            vo.getCollectingInfo()
-              .getCollectingInstitutes()
-              .stream()
-              .map(InstituteVO::getInstituteCode).collect(Collectors.joining(";"))));
-        map.put(COLLNAME, withFieldAsHeader(COLLNAME, vo ->
-            vo.getCollectingInfo()
-              .getCollectingInstitutes()
-              .stream()
-              .map(InstituteVO::getInstituteName)
-              .collect(Collectors.joining(";"))));
-        map.put(COLLINSTADDRESS, withFieldAsHeader(COLLINSTADDRESS, vo ->
-            vo.getCollectingInfo()
-              .getCollectingInstitutes()
-              .stream()
-              .map(InstituteVO::getAddress)
-              .collect(Collectors.joining(";"))));
-        map.put(COLLMISSID, withFieldAsHeader(COLLMISSID, vo -> vo.getCollectingInfo().getCollectingMissionIdentifier()));
-        map.put(GENUS, withFieldAsHeader(GENUS, vo -> vo.getGenus()));
-        map.put(SPECIES, withFieldAsHeader(SPECIES, vo -> vo.getSpecies()));
-        map.put(SPAUTHOR, withFieldAsHeader(SPAUTHOR, vo -> vo.getSpeciesAuthority()));
-        map.put(SUBTAXA, withFieldAsHeader(SUBTAXA, vo -> vo.getSubtaxon()));
-        map.put(SUBTAUTHOR, withFieldAsHeader(SUBTAUTHOR, vo -> vo.getSubtaxonAuthority()));
-        map.put(CROPNAME, withFieldAsHeader(CROPNAME, vo -> vo.getCommonCropName()));
-        map.put(ACCENAME, withFieldAsHeader(ACCENAME, vo -> String.join(";", vo.getAccessionNames())));
-        map.put(ACQDATE, withFieldAsHeader(ACQDATE, vo -> vo.getAcquisitionDate()));
-        map.put(ORIGCTY, withFieldAsHeader(ORIGCTY, vo -> vo.getCountryOfOriginCode()));
-        map.put(COLLSITE, withFieldAsHeader(COLLSITE, vo -> vo.getCollectingInfo().getCollectingSite().getSiteName()));
-        map.put(DECLATITUDE, withFieldAsHeader(DECLATITUDE, vo -> vo.getCollectingInfo().getCollectingSite().getLatitudeDecimal()));
-        map.put(LATITUDE, withFieldAsHeader(LATITUDE, vo -> vo.getCollectingInfo().getCollectingSite().getLatitudeDegrees()));
-        map.put(DECLONGITUDE, withFieldAsHeader(DECLONGITUDE, vo -> vo.getCollectingInfo().getCollectingSite().getLongitudeDecimal()));
-        map.put(LONGITUDE, withFieldAsHeader(LONGITUDE, vo -> vo.getCollectingInfo().getCollectingSite().getLongitudeDegrees()));
-        map.put(COORDUNCERT, withFieldAsHeader(COORDUNCERT, vo -> vo.getCollectingInfo().getCollectingSite().getCoordinateUncertainty()));
-        map.put(COORDDATUM, withFieldAsHeader(COORDDATUM, vo -> vo.getCollectingInfo().getCollectingSite().getSpatialReferenceSystem()));
-        map.put(GEOREFMETH, withFieldAsHeader(GEOREFMETH, vo -> vo.getCollectingInfo().getCollectingSite().getGeoreferencingMethod()));
-        map.put(ELEVATION, withFieldAsHeader(ELEVATION, vo -> vo.getCollectingInfo().getCollectingSite().getElevation()));
-        map.put(COLLDATE, withFieldAsHeader(COLLDATE, vo -> vo.getCollectingInfo().getCollectingDate()));
-        map.put(BREDCODE, withFieldAsHeader(BREDCODE, vo ->
-            vo.getBreedingInstitutes()
-              .stream()
-              .map(InstituteVO::getInstituteCode)
-              .collect(Collectors.joining(";"))));
-        map.put(BREDNAME, withFieldAsHeader(BREDNAME, vo ->
-            vo.getBreedingInstitutes()
-              .stream()
-              .map(InstituteVO::getInstituteName)
-              .collect(Collectors.joining(";"))));
-        map.put(SAMPSTAT, withFieldAsHeader(SAMPSTAT, vo -> vo.getBiologicalStatusOfAccessionCode()));
-        map.put(ANCEST, withFieldAsHeader(ANCEST, vo -> vo.getAncestralData()));
-        map.put(COLLSRC, withFieldAsHeader(COLLSRC, vo -> vo.getAcquisitionSourceCode()));
-        map.put(DONORCODE, withFieldAsHeader(DONORCODE, vo ->
-            vo.getDonorInfo()
-              .stream()
-              .map(donorInfoVO -> donorInfoVO.getDonorInstitute().getInstituteCode())
-              .collect(Collectors.joining(";"))));
-        map.put(DONORNAME, withFieldAsHeader(DONORNAME, vo ->
-            vo.getDonorInfo()
-              .stream()
-              .map(donorInfoVO -> donorInfoVO.getDonorInstitute().getInstituteName())
-              .collect(Collectors.joining(";"))));
-        map.put(DONORNUMB, withFieldAsHeader(DONORNUMB, vo ->
-            vo.getDonorInfo()
-              .stream()
-              .map(DonorInfoVO::getDonorAccessionNumber)
-              .collect(Collectors.joining(";"))));
-        map.put(OTHERNUMB, withFieldAsHeader(OTHERNUMB, vo -> String.join(";", vo.getAlternateIDs())));
-        map.put(DUPLSITE, withFieldAsHeader(DUPLSITE, vo -> null)); // no value available for DUPLSITE
-        map.put(DUPLINSTNAME, withFieldAsHeader(DUPLINSTNAME, vo ->
-            vo.getSafetyDuplicateInstitutes()
-              .stream()
-              .map(InstituteVO::getInstituteName)
-              .collect(Collectors.joining(";"))));
-        map.put(STORAGE, withFieldAsHeader(STORAGE, vo -> String.join(";", vo.getStorageTypeCodes())));
-        map.put(MLSSTAT, withFieldAsHeader(MLSSTAT, vo -> vo.getMlsStatus()));
-        map.put(REMARKS, withFieldAsHeader(REMARKS, vo -> vo.getRemarks()));
+        map.put(DOI, new GermplasmExportableFieldDescriptor("DOI", vo -> vo.getGermplasmPUI()));
+        map.put(ACCESSION_NUMBER, new GermplasmExportableFieldDescriptor("Accession number", vo -> vo.getAccessionNumber()));
+        map.put(ACCESSION_NAME, new GermplasmExportableFieldDescriptor("Accession name", vo -> vo.getGermplasmName()));
+        map.put(TAXON_GROUP, new GermplasmExportableFieldDescriptor("Taxon group", vo -> vo.getCommonCropName()));
+        map.put(HOLDING_INSTITUTION, new GermplasmExportableFieldDescriptor("Holding institution", vo -> vo.getInstituteName()));
+        map.put(LOT_NAME, new GermplasmExportableFieldDescriptor("Lot name", vo -> null));
+        map.put(LOT_SYNONYM, new GermplasmExportableFieldDescriptor("Lot synonym", vo -> null));
+        map.put(COLLECTION_NAME, new GermplasmExportableFieldDescriptor("Collection name", vo -> vo.getCollection().stream().map(
+            CollPopVO::getName).collect(Collectors.joining(", "))));
+        map.put(COLLECTION_TYPE, new GermplasmExportableFieldDescriptor("Collection type", vo -> null));
+        map.put(PANEL_NAME, new GermplasmExportableFieldDescriptor("Panel name", vo -> vo.getPanel().stream().map(CollPopVO::getName).collect(
+            Collectors.joining(", "))));
+        map.put(PANEL_SIZE, new GermplasmExportableFieldDescriptor("Panel size", vo -> null));
 
         this.descriptors = Collections.unmodifiableMap(map);
         if (map.size() != GermplasmExportableField.values().length) {
@@ -120,7 +54,7 @@ public class GermplasmExportService {
         }
     }
 
-    public void export(OutputStream out, Iterator<GermplasmMcpdVO> germplasms, List<GermplasmExportableField> fields) {
+    public void export(OutputStream out, Iterator<GermplasmVO> germplasms, List<GermplasmExportableField> fields) {
         try {
             CSVWriter csvWriter = new CSVWriter(new BufferedWriter(new OutputStreamWriter(out, StandardCharsets.UTF_8)), ';', '"', '\\', "\n");
             String[] header = fields.stream()
@@ -130,7 +64,7 @@ public class GermplasmExportService {
             csvWriter.writeNext(header);
 
             while (germplasms.hasNext()) {
-                GermplasmMcpdVO vo = germplasms.next();
+                GermplasmVO vo = germplasms.next();
                 String[] line =
                     fields.stream()
                           .map(descriptors::get)
@@ -144,17 +78,12 @@ public class GermplasmExportService {
         }
     }
 
-    private GermplasmExportableFieldDescriptor withFieldAsHeader(GermplasmExportableField field,
-                                                                 Function<GermplasmMcpdVO, String> exporter) {
-        return new GermplasmExportableFieldDescriptor(field.name(), exporter);
-    }
-
     private static class GermplasmExportableFieldDescriptor {
         private final String header;
-        private final Function<GermplasmMcpdVO, String> exporter;
+        private final Function<GermplasmVO, String> exporter;
 
         public GermplasmExportableFieldDescriptor(String header,
-                                                  Function<GermplasmMcpdVO, String> exporter) {
+                                                  Function<GermplasmVO, String> exporter) {
             this.header = header;
             this.exporter = exporter;
         }
@@ -163,7 +92,7 @@ public class GermplasmExportService {
             return this.header;
         }
 
-        public String export(GermplasmMcpdVO germplasm) {
+        public String export(GermplasmVO germplasm) {
             return this.exporter.apply(germplasm);
         }
     }
diff --git a/backend/src/main/java/fr/inra/urgi/faidare/web/germplasm/GermplasmExportableField.java b/backend/src/main/java/fr/inra/urgi/faidare/web/germplasm/GermplasmExportableField.java
index 5835bf3aedbf98b67d7d1a0eff4d49f0348b86e7..ecda40cbcdd5fb14532d9b4d9f37eb09b85d90ae 100644
--- a/backend/src/main/java/fr/inra/urgi/faidare/web/germplasm/GermplasmExportableField.java
+++ b/backend/src/main/java/fr/inra/urgi/faidare/web/germplasm/GermplasmExportableField.java
@@ -1,52 +1,20 @@
 package fr.inra.urgi.faidare.web.germplasm;
 
-import com.fasterxml.jackson.annotation.JsonValue;
-
 /**
  * The fields of a germplasm that can be exported
  *
  * @author JB Nizet
  */
 public enum GermplasmExportableField {
-    PUID,
-    INSTCODE,
-    ACCENUMB,
-    COLLNUMB,
-    COLLCODE,
-    COLLNAME,
-    COLLINSTADDRESS,
-    COLLMISSID,
-    GENUS,
-    SPECIES,
-    SPAUTHOR,
-    SUBTAXA,
-    SUBTAUTHOR,
-    CROPNAME,
-    ACCENAME,
-    ACQDATE,
-    ORIGCTY,
-    COLLSITE,
-    DECLATITUDE,
-    LATITUDE,
-    DECLONGITUDE,
-    LONGITUDE,
-    COORDUNCERT,
-    COORDDATUM,
-    GEOREFMETH,
-    ELEVATION,
-    COLLDATE,
-    BREDCODE,
-    BREDNAME,
-    SAMPSTAT,
-    ANCEST,
-    COLLSRC,
-    DONORCODE,
-    DONORNAME,
-    DONORNUMB,
-    OTHERNUMB,
-    DUPLSITE,
-    DUPLINSTNAME,
-    STORAGE,
-    MLSSTAT,
-    REMARKS
+    DOI,
+    ACCESSION_NUMBER,
+    ACCESSION_NAME,
+    TAXON_GROUP,
+    HOLDING_INSTITUTION,
+    LOT_NAME,
+    LOT_SYNONYM,
+    COLLECTION_NAME,
+    COLLECTION_TYPE,
+    PANEL_NAME,
+    PANEL_SIZE
 }
diff --git a/backend/src/main/java/fr/inra/urgi/faidare/web/germplasm/GermplasmMcpdExportCommand.java b/backend/src/main/java/fr/inra/urgi/faidare/web/germplasm/GermplasmMcpdExportCommand.java
new file mode 100644
index 0000000000000000000000000000000000000000..deefb113622f13aa13c55187b2f61a16b96ac041
--- /dev/null
+++ b/backend/src/main/java/fr/inra/urgi/faidare/web/germplasm/GermplasmMcpdExportCommand.java
@@ -0,0 +1,39 @@
+package fr.inra.urgi.faidare.web.germplasm;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import javax.validation.constraints.NotEmpty;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+/**
+ * Command sent to export a list of germplasm MCPD IDs
+ * @author JB Nizet
+ */
+public class GermplasmMcpdExportCommand {
+
+    @NotEmpty
+    private final Set<String> ids;
+
+    /**
+     * The ordered list of fields to export. If empty, all fields are exported
+     */
+    private final List<GermplasmMcpdExportableField> fields;
+
+    @JsonCreator
+    public GermplasmMcpdExportCommand(@JsonProperty("ids") Set<String> ids,
+                                      @JsonProperty("fields") List<GermplasmMcpdExportableField> fields) {
+        this.ids = ids;
+        this.fields = fields == null ? Collections.emptyList() : fields;
+    }
+
+    public Set<String> getIds() {
+        return ids;
+    }
+
+    public List<GermplasmMcpdExportableField> getFields() {
+        return fields;
+    }
+}
diff --git a/backend/src/main/java/fr/inra/urgi/faidare/web/germplasm/GermplasmMcpdExportService.java b/backend/src/main/java/fr/inra/urgi/faidare/web/germplasm/GermplasmMcpdExportService.java
new file mode 100644
index 0000000000000000000000000000000000000000..764eaf9fe40272333e7ac6c725c0c58f24b9d927
--- /dev/null
+++ b/backend/src/main/java/fr/inra/urgi/faidare/web/germplasm/GermplasmMcpdExportService.java
@@ -0,0 +1,178 @@
+package fr.inra.urgi.faidare.web.germplasm;
+
+import static fr.inra.urgi.faidare.web.germplasm.GermplasmMcpdExportableField.*;
+
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.UncheckedIOException;
+import java.nio.charset.StandardCharsets;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+import com.opencsv.CSVWriter;
+import fr.inra.urgi.faidare.domain.data.germplasm.DonorInfoVO;
+import fr.inra.urgi.faidare.domain.data.germplasm.GermplasmMcpdVO;
+import fr.inra.urgi.faidare.domain.data.germplasm.InstituteVO;
+import org.springframework.stereotype.Component;
+
+/**
+ * Service allowing to export germplasm MCPDs as CSV
+ * @author JB Nizet
+ */
+@Component
+public class GermplasmMcpdExportService {
+
+    private final Map<GermplasmMcpdExportableField, GermplasmMcpdExportableFieldDescriptor> descriptors;
+
+    public GermplasmMcpdExportService() {
+        Map<GermplasmMcpdExportableField, GermplasmMcpdExportableFieldDescriptor> map = new HashMap<>();
+
+        map.put(PUID, withFieldAsHeader(PUID, vo -> vo.getGermplasmPUI()));
+        map.put(INSTCODE, withFieldAsHeader(INSTCODE, vo -> vo.getInstituteCode()));
+        map.put(ACCENUMB, withFieldAsHeader(ACCENUMB, vo -> vo.getAccessionNumber()));
+        map.put(COLLNUMB, withFieldAsHeader(COLLNUMB, vo -> vo.getCollectingInfo().getCollectingNumber()));
+        map.put(COLLCODE, withFieldAsHeader(COLLCODE, vo ->
+            vo.getCollectingInfo()
+              .getCollectingInstitutes()
+              .stream()
+              .map(InstituteVO::getInstituteCode).collect(Collectors.joining(";"))));
+        map.put(COLLNAME, withFieldAsHeader(COLLNAME, vo ->
+            vo.getCollectingInfo()
+              .getCollectingInstitutes()
+              .stream()
+              .map(InstituteVO::getInstituteName)
+              .collect(Collectors.joining(";"))));
+        map.put(COLLINSTADDRESS, withFieldAsHeader(COLLINSTADDRESS, vo ->
+            vo.getCollectingInfo()
+              .getCollectingInstitutes()
+              .stream()
+              .map(InstituteVO::getAddress)
+              .collect(Collectors.joining(";"))));
+        map.put(COLLMISSID, withFieldAsHeader(COLLMISSID, vo -> vo.getCollectingInfo().getCollectingMissionIdentifier()));
+        map.put(GENUS, withFieldAsHeader(GENUS, vo -> vo.getGenus()));
+        map.put(SPECIES, withFieldAsHeader(SPECIES, vo -> vo.getSpecies()));
+        map.put(SPAUTHOR, withFieldAsHeader(SPAUTHOR, vo -> vo.getSpeciesAuthority()));
+        map.put(SUBTAXA, withFieldAsHeader(SUBTAXA, vo -> vo.getSubtaxon()));
+        map.put(SUBTAUTHOR, withFieldAsHeader(SUBTAUTHOR, vo -> vo.getSubtaxonAuthority()));
+        map.put(CROPNAME, withFieldAsHeader(CROPNAME, vo -> vo.getCommonCropName()));
+        map.put(ACCENAME, withFieldAsHeader(ACCENAME, vo -> String.join(";", vo.getAccessionNames())));
+        map.put(ACQDATE, withFieldAsHeader(ACQDATE, vo -> vo.getAcquisitionDate()));
+        map.put(ORIGCTY, withFieldAsHeader(ORIGCTY, vo -> vo.getCountryOfOriginCode()));
+        map.put(COLLSITE, withFieldAsHeader(COLLSITE, vo -> vo.getCollectingInfo().getCollectingSite().getSiteName()));
+        map.put(DECLATITUDE, withFieldAsHeader(DECLATITUDE, vo -> vo.getCollectingInfo().getCollectingSite().getLatitudeDecimal()));
+        map.put(LATITUDE, withFieldAsHeader(LATITUDE, vo -> vo.getCollectingInfo().getCollectingSite().getLatitudeDegrees()));
+        map.put(DECLONGITUDE, withFieldAsHeader(DECLONGITUDE, vo -> vo.getCollectingInfo().getCollectingSite().getLongitudeDecimal()));
+        map.put(LONGITUDE, withFieldAsHeader(LONGITUDE, vo -> vo.getCollectingInfo().getCollectingSite().getLongitudeDegrees()));
+        map.put(COORDUNCERT, withFieldAsHeader(COORDUNCERT, vo -> vo.getCollectingInfo().getCollectingSite().getCoordinateUncertainty()));
+        map.put(COORDDATUM, withFieldAsHeader(COORDDATUM, vo -> vo.getCollectingInfo().getCollectingSite().getSpatialReferenceSystem()));
+        map.put(GEOREFMETH, withFieldAsHeader(GEOREFMETH, vo -> vo.getCollectingInfo().getCollectingSite().getGeoreferencingMethod()));
+        map.put(ELEVATION, withFieldAsHeader(ELEVATION, vo -> vo.getCollectingInfo().getCollectingSite().getElevation()));
+        map.put(COLLDATE, withFieldAsHeader(COLLDATE, vo -> vo.getCollectingInfo().getCollectingDate()));
+        map.put(BREDCODE, withFieldAsHeader(BREDCODE, vo ->
+            vo.getBreedingInstitutes()
+              .stream()
+              .map(InstituteVO::getInstituteCode)
+              .collect(Collectors.joining(";"))));
+        map.put(BREDNAME, withFieldAsHeader(BREDNAME, vo ->
+            vo.getBreedingInstitutes()
+              .stream()
+              .map(InstituteVO::getInstituteName)
+              .collect(Collectors.joining(";"))));
+        map.put(SAMPSTAT, withFieldAsHeader(SAMPSTAT, vo -> vo.getBiologicalStatusOfAccessionCode()));
+        map.put(ANCEST, withFieldAsHeader(ANCEST, vo -> vo.getAncestralData()));
+        map.put(COLLSRC, withFieldAsHeader(COLLSRC, vo -> vo.getAcquisitionSourceCode()));
+        map.put(DONORCODE, withFieldAsHeader(DONORCODE, vo ->
+            vo.getDonorInfo()
+              .stream()
+              .map(donorInfoVO -> donorInfoVO.getDonorInstitute().getInstituteCode())
+              .collect(Collectors.joining(";"))));
+        map.put(DONORNAME, withFieldAsHeader(DONORNAME, vo ->
+            vo.getDonorInfo()
+              .stream()
+              .map(donorInfoVO -> donorInfoVO.getDonorInstitute().getInstituteName())
+              .collect(Collectors.joining(";"))));
+        map.put(DONORNUMB, withFieldAsHeader(DONORNUMB, vo ->
+            vo.getDonorInfo()
+              .stream()
+              .map(DonorInfoVO::getDonorAccessionNumber)
+              .collect(Collectors.joining(";"))));
+        map.put(OTHERNUMB, withFieldAsHeader(OTHERNUMB, vo -> String.join(";", vo.getAlternateIDs())));
+        map.put(DUPLSITE, withFieldAsHeader(DUPLSITE, vo ->
+            vo.getSafetyDuplicateInstitutes()
+              .stream()
+              .map(InstituteVO::getInstituteCode)
+              .collect(Collectors.joining(";"))));
+        map.put(DUPLINSTNAME, withFieldAsHeader(DUPLINSTNAME, vo ->
+            vo.getSafetyDuplicateInstitutes()
+              .stream()
+              .map(InstituteVO::getInstituteName)
+              .collect(Collectors.joining(";"))));
+        map.put(STORAGE, withFieldAsHeader(STORAGE, vo -> String.join(";", vo.getStorageTypeCodes())));
+        map.put(MLSSTAT, withFieldAsHeader(MLSSTAT, vo -> vo.getMlsStatus()));
+        map.put(REMARKS, withFieldAsHeader(REMARKS, vo -> vo.getRemarks()));
+
+        this.descriptors = Collections.unmodifiableMap(map);
+
+        if (map.size() != GermplasmMcpdExportableField.values().length) {
+            throw new IllegalStateException("Missing field descriptor");
+        }
+    }
+
+    public void export(OutputStream out, Iterator<GermplasmMcpdVO> germplasms, List<GermplasmMcpdExportableField> fields) {
+        try {
+            CSVWriter csvWriter = new CSVWriter(new BufferedWriter(new OutputStreamWriter(out, StandardCharsets.UTF_8)), ';', '"', '\\', "\n");
+            String[] header = fields.stream()
+                                    .map(descriptors::get)
+                                    .map(GermplasmMcpdExportableFieldDescriptor::getHeader)
+                                    .toArray(String[]::new);
+            csvWriter.writeNext(header);
+
+            while (germplasms.hasNext()) {
+                GermplasmMcpdVO vo = germplasms.next();
+                String[] line =
+                    fields.stream()
+                          .map(descriptors::get)
+                          .map(descriptor -> descriptor.export(vo))
+                          .toArray(String[]::new);
+                csvWriter.writeNext(line);
+            }
+            csvWriter.flush();
+        } catch (IOException e) {
+            throw new UncheckedIOException(e);
+        }
+    }
+
+    private GermplasmMcpdExportableFieldDescriptor withFieldAsHeader(
+        GermplasmMcpdExportableField field,
+        Function<GermplasmMcpdVO, String> exporter) {
+        return new GermplasmMcpdExportableFieldDescriptor(field.name(), exporter);
+    }
+
+    private static class GermplasmMcpdExportableFieldDescriptor {
+        private final String header;
+        private final Function<GermplasmMcpdVO, String> exporter;
+
+        public GermplasmMcpdExportableFieldDescriptor(String header,
+                                                      Function<GermplasmMcpdVO, String> exporter) {
+            this.header = header;
+            this.exporter = exporter;
+        }
+
+        public String getHeader() {
+            return this.header;
+        }
+
+        public String export(GermplasmMcpdVO germplasm) {
+            return this.exporter.apply(germplasm);
+        }
+    }
+}
+
+
diff --git a/backend/src/main/java/fr/inra/urgi/faidare/web/germplasm/GermplasmMcpdExportableField.java b/backend/src/main/java/fr/inra/urgi/faidare/web/germplasm/GermplasmMcpdExportableField.java
new file mode 100644
index 0000000000000000000000000000000000000000..860003663cd5b5397bf1b7f59434e98acb46f972
--- /dev/null
+++ b/backend/src/main/java/fr/inra/urgi/faidare/web/germplasm/GermplasmMcpdExportableField.java
@@ -0,0 +1,52 @@
+package fr.inra.urgi.faidare.web.germplasm;
+
+import com.fasterxml.jackson.annotation.JsonValue;
+
+/**
+ * The fields of a germplasm MCPD that can be exported
+ *
+ * @author JB Nizet
+ */
+public enum GermplasmMcpdExportableField {
+    PUID,
+    INSTCODE,
+    ACCENUMB,
+    COLLNUMB,
+    COLLCODE,
+    COLLNAME,
+    COLLINSTADDRESS,
+    COLLMISSID,
+    GENUS,
+    SPECIES,
+    SPAUTHOR,
+    SUBTAXA,
+    SUBTAUTHOR,
+    CROPNAME,
+    ACCENAME,
+    ACQDATE,
+    ORIGCTY,
+    COLLSITE,
+    DECLATITUDE,
+    LATITUDE,
+    DECLONGITUDE,
+    LONGITUDE,
+    COORDUNCERT,
+    COORDDATUM,
+    GEOREFMETH,
+    ELEVATION,
+    COLLDATE,
+    BREDCODE,
+    BREDNAME,
+    SAMPSTAT,
+    ANCEST,
+    COLLSRC,
+    DONORCODE,
+    DONORNAME,
+    DONORNUMB,
+    OTHERNUMB,
+    DUPLSITE,
+    DUPLINSTNAME,
+    STORAGE,
+    MLSSTAT,
+    REMARKS
+}
diff --git a/backend/src/test/java/fr/inra/urgi/faidare/repository/es/GermplasmRepositoryTest.java b/backend/src/test/java/fr/inra/urgi/faidare/repository/es/GermplasmRepositoryTest.java
index 7ec9bb8e27fa23dbdb2a84b87e5c116334fae20c..4ba1d45b622eea6310e5e7bb99bc234201ace801 100644
--- a/backend/src/test/java/fr/inra/urgi/faidare/repository/es/GermplasmRepositoryTest.java
+++ b/backend/src/test/java/fr/inra/urgi/faidare/repository/es/GermplasmRepositoryTest.java
@@ -28,6 +28,7 @@ import fr.inra.urgi.faidare.domain.data.germplasm.GermplasmMcpdVO;
 
 import java.util.Arrays;
 import java.util.Collections;
+import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Objects;
@@ -140,7 +141,7 @@ class GermplasmRepositoryTest {
     void shouldScrollGermplasmMcpdsByIds() {
         Iterator<GermplasmMcpdVO> list = repository.scrollGermplasmMcpdsByIds(Collections.singleton("13705"), 1000);
         assertThat(list).toIterable()
-                        .isNotEmpty()
+                        .hasSize(1)
                         .allMatch(vo -> !vo.getGermplasmDbId().isEmpty());
 
         list = repository.scrollGermplasmMcpdsByIds(Collections.singleton("1370"), 1000);
@@ -152,6 +153,27 @@ class GermplasmRepositoryTest {
                         .isEmpty();
     }
 
+    @Test
+    void shouldScrollGermplasmsByIds() {
+        Iterator<GermplasmVO> list = repository.scrollGermplasmsByIds(
+            new HashSet<>(
+                Arrays.asList(
+                    "ZG9pOjEwLjE1NDU0LzEuNDkyMTc4NjM4MTc4MzY5NkUxMg==",
+                    "ZG9pOjEwLjE1NDU0LzEuNDkyMTc4NjM4NDcyNjA1MkUxMg==")
+            ), 1000);
+        assertThat(list).toIterable()
+                        .hasSize(2)
+                        .allMatch(vo -> !vo.getGermplasmDbId().isEmpty());
+
+        list = repository.scrollGermplasmsByIds(Collections.singleton("ZG9pOjEwL"), 1000);
+        assertThat(list).toIterable()
+                        .isEmpty();
+
+        list = repository.scrollGermplasmsByIds(Collections.singleton("ZG9pOjEwL"), 1000);
+        assertThat(list).toIterable()
+                        .isEmpty();
+    }
+
     @Test
     void should_Scroll_By_accessionNumber() {
         GermplasmPOSTSearchCriteria criteria = new GermplasmPOSTSearchCriteria();
diff --git a/backend/src/test/java/fr/inra/urgi/faidare/web/germplasm/GermplasmControllerTest.java b/backend/src/test/java/fr/inra/urgi/faidare/web/germplasm/GermplasmControllerTest.java
index 47695c75fe427ea226d278736d0ed05fd7c1e38b..37595c9b7669d49f357f49a4f365ebc12d0a3561 100644
--- a/backend/src/test/java/fr/inra/urgi/faidare/web/germplasm/GermplasmControllerTest.java
+++ b/backend/src/test/java/fr/inra/urgi/faidare/web/germplasm/GermplasmControllerTest.java
@@ -8,7 +8,6 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
 import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
 
 import java.util.Arrays;
-import java.util.Collections;
 import java.util.List;
 
 import com.fasterxml.jackson.databind.ObjectMapper;
@@ -40,7 +39,7 @@ import org.springframework.test.web.servlet.MvcResult;
  * @author JB Nizet
  */
 @WebMvcTest(GermplasmController.class)
-@Import(GermplasmExportService.class)
+@Import({GermplasmMcpdExportService.class, GermplasmExportService.class})
 public class GermplasmControllerTest {
 
     @Autowired
@@ -131,20 +130,20 @@ public class GermplasmControllerTest {
     }
 
     @Test
-    void shouldExportGermplasms() throws Exception {
+    void shouldExportMcpds() throws Exception {
         List<GermplasmMcpdVO> germplasms = Arrays.asList(
             Fixtures.createGermplasmMcpd(),
             Fixtures.createGermplasmMcpd()
         );
 
-        GermplasmExportCommand command = new GermplasmExportCommand(
+        GermplasmMcpdExportCommand command = new GermplasmMcpdExportCommand(
             Sets.newHashSet("g1", "g2"),
-            Arrays.asList(GermplasmExportableField.PUID, GermplasmExportableField.INSTCODE));
+            Arrays.asList(GermplasmMcpdExportableField.PUID, GermplasmMcpdExportableField.INSTCODE));
 
         when(mockGermplasmRepository.scrollGermplasmMcpdsByIds(eq(command.getIds()), anyInt()))
             .thenAnswer(invocation -> germplasms.iterator());
 
-        MvcResult mvcResult = mockMvc.perform(post("/germplasms/exports")
+        MvcResult mvcResult = mockMvc.perform(post("/germplasms/exports/mcpd")
                                                   .contentType(MediaType.APPLICATION_JSON)
                                                   .content(objectMapper.writeValueAsBytes(
                                                       command)))
@@ -159,6 +158,37 @@ public class GermplasmControllerTest {
                                                "\"PUI1\";\"Inst1\"\n"));
     }
 
+    @Test
+    void shouldExporPlantMaterials() throws Exception {
+        List<GermplasmVO> germplasms = Arrays.asList(
+            Fixtures.createGermplasm(),
+            Fixtures.createGermplasm()
+        );
+
+        GermplasmExportCommand command = new GermplasmExportCommand(
+            Sets.newHashSet("g1", "g2"),
+            Arrays.asList(GermplasmExportableField.DOI,
+                          GermplasmExportableField.ACCESSION_NUMBER,
+                          GermplasmExportableField.ACCESSION_NAME));
+
+        when(mockGermplasmRepository.scrollGermplasmsByIds(eq(command.getIds()), anyInt()))
+            .thenAnswer(invocation -> germplasms.iterator());
+
+        MvcResult mvcResult = mockMvc.perform(post("/germplasms/exports/plant-material")
+                                                  .contentType(MediaType.APPLICATION_JSON)
+                                                  .content(objectMapper.writeValueAsBytes(
+                                                      command)))
+                                     .andExpect(request().asyncStarted())
+                                     .andReturn();
+
+        this.mockMvc.perform(asyncDispatch(mvcResult))
+                    .andExpect(status().isOk())
+                    .andExpect(content().contentType("text/csv"))
+                    .andExpect(content().string("\"DOI\";\"Accession number\";\"Accession name\"\n" +
+                                                    "\"germplasmPUI\";\"1408\";\"BLE BARBU DU ROUSSILLON\"\n" +
+                                                    "\"germplasmPUI\";\"1408\";\"BLE BARBU DU ROUSSILLON\"\n"));
+    }
+
     private void testSitemap(int index, String expectedContent) throws Exception {
         MvcResult mvcResult = mockMvc.perform(get("/faidare/germplasms/sitemap-" + index + ".txt")
                                                   .contextPath("/faidare"))
@@ -169,6 +199,5 @@ public class GermplasmControllerTest {
                     .andExpect(status().isOk())
                     .andExpect(content().contentType(MediaType.TEXT_PLAIN))
                     .andExpect(content().string(expectedContent));
-
     }
 }