diff --git a/src/api/dataset.ts b/src/api/dataset.ts
index 89cb9bf4d27a748c20703133d689aa5bc0394e77..9db7186d45bd3b69a3cee5cdb69a0101047c6502 100644
--- a/src/api/dataset.ts
+++ b/src/api/dataset.ts
@@ -1,13 +1,15 @@
 import axios from 'axios'
 import { CursorPaginationParameters, PageNumberPaginationParameters, unique } from '.'
 import { CursorPagination, PageNumberPagination, UUID } from '@/types'
-import { Dataset, DatasetElementList, ElementDataset } from '@/types/dataset'
+import { Dataset, DatasetElementList, ElementDatasetSet } from '@/types/dataset'
 
 export interface DatasetListParameters extends CursorPaginationParameters { set?: string | null }
 
-export type DatasetEdit = Pick<Dataset, 'id' | 'name' | 'description' | 'sets'>
+export type DatasetEdit = Pick<Dataset, 'id' | 'name' | 'description'>
 
-export type DatasetCreate = Omit<DatasetEdit, 'id'>
+export interface DatasetCreate extends Pick<Dataset, 'name' | 'description'> {
+  set_names: string[]
+}
 
 export const listCorpusDataset = unique(
   async (corpusId: UUID, params: PageNumberPaginationParameters = {}): Promise<PageNumberPagination<Dataset>> =>
@@ -34,9 +36,9 @@ export const deleteDataset = unique(
 )
 
 // List datasets containing a specific element
-export const listElementDatasets = unique(
-  async (elementId: UUID, params: PageNumberPaginationParameters = {}): Promise<PageNumberPagination<ElementDataset>> =>
-    (await axios.get(`/element/${elementId}/datasets/`, { params })).data
+export const listElementDatasetSets = unique(
+  async (elementId: UUID, params: PageNumberPaginationParameters = {}): Promise<PageNumberPagination<ElementDatasetSet>> =>
+    (await axios.get(`/element/${elementId}/sets/`, { params })).data
 )
 
 export const listDatasetElements = unique(
@@ -51,3 +53,15 @@ export const deleteDatasetElement = unique(
 export const cloneDataset = unique(
   async (datasetId: UUID): Promise<Dataset> => (await axios.post(`datasets/${datasetId}/clone/`)).data
 )
+
+export const createDatasetSet = unique(
+  async (datasetId: UUID, name: string) => (await axios.post(`datasets/${datasetId}/sets/`, { name: name })).data
+)
+
+export const updateDatasetSet = unique(
+  async (datasetId: UUID, setId: UUID, name: string) => (await axios.patch(`datasets/${datasetId}/sets/${setId}/`, { name: name })).data
+)
+
+export const deleteDatasetSet = unique(
+  async (datasetId: UUID, setId: UUID) => await axios.delete(`datasets/${datasetId}/sets/${setId}/`)
+)
diff --git a/src/api/process.js b/src/api/process.js
index dfd5aca0e0232bdd353d8049af65d79e037ac63b..e8a873a6830cdf96195ff771e940ceaad46a62c1 100644
--- a/src/api/process.js
+++ b/src/api/process.js
@@ -40,10 +40,8 @@ export const clearProcess = unique(async id => await axios.delete(`/process/${id
 // Select elements with a worker activity failure on the process
 export const selectProcessFailures = unique(async id => await axios.post(`/process/${id}/select-failures/`))
 
-export const listProcessDatasets = unique(async ({ processId, ...params }) => (await axios.get(`/process/${processId}/datasets/`, { params })).data)
+export const listProcessSets = unique(async ({ processId, ...params }) => (await axios.get(`/process/${processId}/sets/`, { params })).data)
 
-export const createProcessDataset = unique((processId, datasetId, payload) => axios.post(`/process/${processId}/dataset/${datasetId}/`, payload))
+export const createProcessSet = unique((processId, setId, payload) => axios.post(`/process/${processId}/set/${setId}/`, payload))
 
-export const updateProcessDataset = unique((processId, datasetId, payload) => axios.patch(`/process/${processId}/dataset/${datasetId}/`, payload))
-
-export const deleteProcessDataset = unique((processId, datasetId) => axios.delete(`/process/${processId}/dataset/${datasetId}/`))
+export const deleteProcessSet = unique((processId, setId) => axios.delete(`/process/${processId}/set/${setId}/`))
diff --git a/src/api/selection.ts b/src/api/selection.ts
index 097b49489207679833c5ecd0a6469db5b54fb7d0..f0623d603984d6134763804158de70dfcdc28694 100644
--- a/src/api/selection.ts
+++ b/src/api/selection.ts
@@ -2,19 +2,7 @@ import axios from 'axios'
 import { unique } from '.'
 import { UUID } from '@/types'
 
-export interface CreateDatasetElementsSelectionParameters {
-  /**
-   * ID of the dataset that will receive selected elements
-   */
-  dataset_id: UUID
-
-  /**
-   * Name of the set elements will be added to
-   */
-  set: string
-}
-
 /**
- * Add selected elements to a corpus' dataset
+ * Add selected elements to a corpus' dataset set
  */
-export const createDatasetElementsSelection = unique(async (corpusId: UUID, params: CreateDatasetElementsSelectionParameters) => (await axios.post(`/corpus/${corpusId}/datasets/selection/`, params)))
+export const createDatasetElementsSelection = unique(async (corpusId: UUID, setId: UUID) => (await axios.post(`/corpus/${corpusId}/datasets/selection/`, { set_id: setId })))
diff --git a/src/components/Corpus/Datasets/DatasetSets/CreateForm.vue b/src/components/Corpus/Datasets/DatasetSets/CreateForm.vue
new file mode 100644
index 0000000000000000000000000000000000000000..217ea114900663c87531112b5977e8cb273997ea
--- /dev/null
+++ b/src/components/Corpus/Datasets/DatasetSets/CreateForm.vue
@@ -0,0 +1,104 @@
+<template>
+  <div class="field is-grouped mt-1 mb-0">
+    <div class="control mr-1">
+      <input
+        class="input"
+        type="text"
+        v-model="setName"
+        :class="{ 'is-danger': createError.name }"
+        :disabled="!hasContribPrivilege || dataset.state !== 'open'"
+      />
+    </div>
+    <div class="control">
+      <button
+        class="button is-primary"
+        :class="{ 'is-loading': loading }"
+        :disabled="loading || !canCreate || undefined"
+        v-on:click="setCreate"
+        :title="createTitle"
+      >
+        <i class="icon-plus"></i>
+      </button>
+    </div>
+  </div>
+  <template v-if="createError.name">
+    <p v-for="(err, i) in createError.name" :key="i" class="help has-text-danger">{{ err }}</p>
+  </template>
+</template>
+
+<script lang="ts">
+import { mapGetters as mapVuexGetters } from 'vuex'
+import { corporaMixin, truncateMixin } from '@/mixins'
+import { PropType, defineComponent } from 'vue'
+import { Dataset } from '@/types/dataset'
+import { mapActions } from 'pinia'
+import { useDatasetStore, useNotificationStore } from '@/stores'
+import { isAxiosError } from 'axios'
+
+export default defineComponent({
+  mixins: [corporaMixin, truncateMixin],
+  props: {
+    dataset: {
+      type: Object as PropType<Dataset>,
+      required: true
+    },
+    // Used to reset the set name field when the modal is closed
+    modalOpen: {
+      type: Boolean,
+      required: true
+    }
+  },
+  data: () => ({
+    loading: false,
+    setName: '',
+    createError: { name: '' }
+  }),
+  computed: {
+    ...mapVuexGetters('auth', ['isVerified']),
+    hasContribPrivilege () {
+      return this.isVerified && this.corpus && this.canWrite(this.corpus)
+    },
+    canCreate () {
+      return this.setName.trim() && this.hasContribPrivilege && this.dataset.state === 'open'
+    },
+    corpusId () {
+      return this.dataset.corpus_id
+    },
+    createTitle () {
+      if (this.dataset.state !== 'open') return 'Sets can only be created in open datasets'
+      else if (!this.hasContribPrivilege) return 'You must be a contributor on the project to create a set'
+      else if (this.canCreate) return 'Create new set'
+      return ''
+    }
+  },
+  methods: {
+    ...mapActions(useDatasetStore, ['createDatasetSet']),
+    ...mapActions(useNotificationStore, ['notify']),
+    async setCreate () {
+      this.loading = true
+      if (!this.canCreate) return
+      try {
+        await this.createDatasetSet(this.dataset.id, this.setName)
+        this.notify({ type: 'success', text: `Dataset set ${this.truncateShort(this.setName)} created` })
+        this.createError.name = ''
+        this.setName = ''
+      } catch (err) {
+        if (isAxiosError(err) && err.response?.status === 400 && err.response.data && 'name' in err.response.data) {
+          this.createError.name = err.response.data.name
+        }
+      } finally {
+        this.loading = false
+      }
+    }
+  },
+  watch: {
+    modalOpen (newValue) {
+      // Reset set name field and errors when the modal is closed
+      if (newValue === false) {
+        this.setName = ''
+        this.createError.name = ''
+      }
+    }
+  }
+})
+</script>
diff --git a/src/components/Corpus/Datasets/DatasetSets/Row.vue b/src/components/Corpus/Datasets/DatasetSets/Row.vue
new file mode 100644
index 0000000000000000000000000000000000000000..4abf78b809ec86545708e7e667d76b2e1713f896
--- /dev/null
+++ b/src/components/Corpus/Datasets/DatasetSets/Row.vue
@@ -0,0 +1,144 @@
+<template>
+  <div class="field is-grouped mt-1 mb-0">
+    <div class="control mr-1">
+      <input
+        class="input"
+        type="text"
+        v-model="setName"
+        :class="{ 'is-danger': updateError.name }"
+        :disabled="!hasContribPrivilege || dataset.state !== 'open'"
+      />
+    </div>
+    <div class="control mr-1">
+      <button
+        class="button has-text-primary"
+        :class="{ 'is-loading': loading }"
+        :disabled="loading || !canUpdate || undefined"
+        v-on:click="setUpdate"
+        :title="updateTitle"
+      >
+        <i class="icon-edit"></i>
+      </button>
+    </div>
+    <div class="control">
+      <button
+        class="button has-text-danger"
+        :class="{ 'is-loading': loading }"
+        :disabled="loading || !canDelete || undefined"
+        v-on:click="setDelete"
+        :title="deleteTitle"
+      >
+        <i class="icon-trash"></i>
+      </button>
+    </div>
+  </div>
+  <template v-if="updateError.name">
+    <p v-for="(err, i) in updateError.name" :key="i" class="help has-text-danger">{{ err }}</p>
+  </template>
+</template>
+
+<script lang="ts">
+import { mapGetters as mapVuexGetters } from 'vuex'
+import { corporaMixin } from '@/mixins'
+import { PropType, defineComponent } from 'vue'
+import { DatasetSet, Dataset } from '@/types/dataset'
+import { mapActions } from 'pinia'
+import { useDatasetStore, useNotificationStore } from '@/stores'
+import { isAxiosError } from 'axios'
+
+export default defineComponent({
+  mixins: [corporaMixin],
+  props: {
+    datasetSet: {
+      type: Object as PropType<DatasetSet>,
+      required: true
+    },
+    dataset: {
+      type: Object as PropType<Dataset>,
+      required: true
+    },
+    // Used to reset the set name field when the modal is closed
+    modalOpen: {
+      type: Boolean,
+      required: true
+    }
+  },
+  data: () => ({
+    loading: false,
+    setName: '',
+    updateError: { name: '' }
+  }),
+  mounted () {
+    this.setName = this.datasetSet.name
+  },
+  computed: {
+    ...mapVuexGetters('auth', ['isVerified']),
+    hasContribPrivilege () {
+      return this.isVerified && this.corpus && this.canWrite(this.corpus)
+    },
+    hasAdminPrivilege () {
+      return this.isVerified && this.corpus && this.canAdmin(this.corpus)
+    },
+    canUpdate () {
+      return this.datasetSet.name !== this.setName && this.hasContribPrivilege && this.dataset.state === 'open'
+    },
+    canDelete () {
+      return this.hasAdminPrivilege && this.dataset.state === 'open' && this.dataset.sets.length > 1
+    },
+    corpusId () {
+      return this.dataset.corpus_id
+    },
+    updateTitle () {
+      if (this.dataset.state !== 'open') return 'Sets can only be edited on open datasets'
+      else if (!this.hasContribPrivilege) return "You must be a contributor on the project to edit a dataset's sets"
+      else if (this.canUpdate) return 'Update set name'
+      return undefined
+    },
+    deleteTitle () {
+      if (this.dataset.state !== 'open') return 'Sets can only be deleted on open datasets'
+      else if (!this.hasAdminPrivilege) return 'You must be an administrator on the project to delete dataset sets'
+      else if (this.dataset.sets.length <= 1) return 'This is the only set in the dataset and cannot be deleted'
+      return 'Delete set'
+    }
+  },
+  methods: {
+    ...mapActions(useDatasetStore, ['updateDatasetSet', 'deleteDatasetSet']),
+    ...mapActions(useNotificationStore, ['notify']),
+    async setUpdate () {
+      if (!this.canUpdate) return
+      this.loading = true
+      try {
+        await this.updateDatasetSet(this.dataset.id, this.datasetSet.id, this.setName)
+        this.notify({ type: 'success', text: 'Dataset set updated' })
+        this.updateError.name = ''
+      } catch (err) {
+        if (isAxiosError(err) && err.response?.status === 400 && err.response.data && 'name' in err.response.data) {
+          this.updateError.name = err.response.data.name
+        }
+      } finally {
+        this.loading = false
+      }
+    },
+    async setDelete () {
+      this.loading = true
+      if (!this.canDelete) return
+      // Errors are handled (notification) in the store
+      try {
+        await this.deleteDatasetSet(this.dataset.id, this.datasetSet.id)
+        this.notify({ type: 'success', text: 'Dataset set deleted' })
+      } finally {
+        this.loading = false
+      }
+    }
+  },
+  watch: {
+    modalOpen (newValue) {
+      // Reset set name field and errors when the modal is closed
+      if (newValue === false) {
+        this.setName = this.datasetSet.name
+        this.updateError.name = ''
+      }
+    }
+  }
+})
+</script>
diff --git a/src/components/Corpus/Datasets/EditModal.vue b/src/components/Corpus/Datasets/EditModal.vue
index 702309493354c894236aae3c0a19cd22f3409043..4a2decb68153e2787c6bd05faf05ef9d54c94cb6 100644
--- a/src/components/Corpus/Datasets/EditModal.vue
+++ b/src/components/Corpus/Datasets/EditModal.vue
@@ -3,83 +3,80 @@
     :model-value="modelValue"
     v-on:update:model-value="value => $emit('update:modelValue', value)"
     :title="modalTitle"
+    is-large
   >
     <form v-on:submit.prevent="performCreate">
-      <div class="field is-horizontal">
-        <div class="field-label is-normal">
-          <label class="label">Name</label>
-        </div>
-        <div class="field-body">
-          <div class="field">
-            <div class="control">
-              <input
-                class="input"
-                v-model="newDataset.name"
-                type="text"
-                placeholder="Dataset name"
-              />
-            </div>
-            <template v-if="fieldErrors.name">
-              <p class="help is-danger">{{ fieldErrors.name }}</p>
-            </template>
-          </div>
+      <div class="field">
+        <label class="label">Name</label>
+        <div class="control is-expanded">
+          <input
+            class="input"
+            :class="{ 'is-danger': fieldErrors.name }"
+            v-model="newDataset.name"
+            type="text"
+            placeholder="Dataset name"
+          />
         </div>
+        <template v-if="fieldErrors.name">
+          <p class="help is-danger">{{ fieldErrors.name }}</p>
+        </template>
       </div>
-      <div class="field is-horizontal">
-        <div class="field-label is-normal">
-          <label class="label">Description</label>
-        </div>
-        <div class="field-body">
-          <div class="field">
-            <div class="control">
-              <textarea
-                class="textarea"
-                v-model="newDataset.description"
-                type="text"
-                placeholder="Dataset description"
-              ></textarea>
-            </div>
-            <template v-if="fieldErrors.description">
-              <p class="help is-danger">{{ fieldErrors.description }}</p>
-            </template>
-          </div>
+      <div class="field">
+        <label class="label">Description</label>
+        <div class="control is-expanded">
+          <textarea
+            class="textarea"
+            :class="{ 'is-danger': fieldErrors.description }"
+            v-model="newDataset.description"
+            placeholder="Dataset description"
+          ></textarea>
         </div>
+        <template v-if="fieldErrors.description">
+          <p class="help is-danger">{{ fieldErrors.description }}</p>
+        </template>
       </div>
-      <div class="field is-horizontal">
-        <div class="field-label is-normal">
-          <label class="label">Sets</label>
-        </div>
+      <div class="field">
+        <label class="label">Sets</label>
         <div class="field-body">
-          <div class="field">
-            <div class="control">
-              <input
-                class="input"
-                v-model="newDataset.sets"
-                type="text"
-                placeholder="Dataset sets"
-              />
-            </div>
-            <template v-if="fieldErrors.sets">
-              <p v-if="!(typeof fieldErrors.sets === 'string')">
-                <span
-                  class="help is-danger"
-                  v-for="(v, k) in fieldErrors.sets"
-                  :key="k"
-                >
-                  {{ k }}: {{ v }}
-                </span>
-              </p>
-              <p v-else>
-                <span class="help is-danger">{{ fieldErrors.sets }}</span>
-              </p>
+          <div>
+            <!-- Sets field for dataset edition -->
+            <template v-if="datasetInstance">
+              <div v-for="dss in datasetInstance.sets" :key="dss.id">
+                <DatasetSet :dataset-set="dss" :dataset="datasetInstance" :modal-open="modelValue" />
+              </div>
+              <AddSetForm :dataset="datasetInstance" :modal-open="modelValue" />
             </template>
-            <p class="help">
-              Enter the set names, separated by commas.
-              <!-- This default value only applies when creating, not updating -->
-              <template v-if="!datasetInstance">
-                <br />If this field is left empty, the created sets will be training, test and validation.
+            <!-- Sets field for dataset creation -->
+            <template v-else>
+              <div
+                class="control"
+                v-for="(item, i) in newDataset.sets"
+                :key="i"
+              >
+                <div class="field mt-1 mb-0">
+                  <input
+                    class="input"
+                    :class="{ 'is-danger': fieldErrors.set_names }"
+                    type="text"
+                    :value="item"
+                    v-on:change="updateSetNames(i, $event.target.value)"
+                  />
+                </div>
+              </div>
+              <div>
+                <button
+                  class="button is-primary mt-1"
+                  :class="{ 'is-loading': loading }"
+                  :disabled="loading || !hasContribPrivilege || undefined"
+                  v-on:click="addSetField"
+                >
+                  <i class="icon-plus"></i>
+                </button>
+              </div>
+              <template v-if="fieldErrors.set_names">
+                <p class="help is-danger">{{ fieldErrors.set_names }}</p>
               </template>
-            </p>
+            </template>
           </div>
         </div>
       </div>
@@ -89,7 +86,7 @@
       <button
         type="submit"
         class="button is-primary"
-        :class="{ 'is-loading': createLoading }"
+        :class="{ 'is-loading': loading }"
         v-on:click="save"
         :disabled="!canSave"
         :title="saveButtonTitle"
@@ -109,11 +106,15 @@ import { isEmpty } from 'lodash'
 import Modal from '@/components/Modal.vue'
 import { errorParser } from '@/helpers'
 import { useDatasetStore, useNotificationStore } from '@/stores'
+import DatasetSet from '@/components/Corpus/Datasets/DatasetSets/Row.vue'
+import AddSetForm from '@/components/Corpus/Datasets/DatasetSets/CreateForm.vue'
 
 export default {
   mixins: [corporaMixin, truncateMixin],
   components: {
-    Modal
+    Modal,
+    DatasetSet,
+    AddSetForm
   },
   emits: ['dataset-action', 'update:modelValue'],
   props: {
@@ -134,9 +135,9 @@ export default {
     newDataset: {
       name: '',
       description: '',
-      sets: ''
+      sets: []
     },
-    createLoading: false,
+    loading: false,
     fieldErrors: {}
   }),
   mounted () {
@@ -144,7 +145,7 @@ export default {
       this.newDataset = {
         name: this.datasetInstance.name,
         description: this.datasetInstance.description,
-        sets: this.datasetInstance.sets.join(', ')
+        sets: this.datasetInstance.sets
       }
     }
   },
@@ -155,14 +156,7 @@ export default {
     },
     modalTitle () {
       if (!this.datasetInstance) return 'Create a new dataset'
-      else return `Edit dataset ${this.truncateShort(this.datasetInstance.name)}`
-    },
-    setList () {
-      // split the input of the sets field on ',' to make a list
-      let setList = this.newDataset.sets.split(',')
-      // trim list items and ignore empty items
-      setList = setList.map(item => item.trim()).filter(item => item.length > 0)
-      return setList
+      else return `Edit dataset ${this.truncateLong(this.datasetInstance.name)}`
     },
     canSave () {
       return this.newDataset.name.trim() && this.newDataset.description.trim()
@@ -177,13 +171,17 @@ export default {
     ...mapActions(useDatasetStore, ['createCorpusDataset', 'updateCorpusDataset']),
     ...mapActions(useNotificationStore, ['notify']),
     async save () {
-      if (!this.hasContribPrivilege || this.createLoading || this.invalidForm) { return }
-      this.createLoading = true
+      if (!this.hasContribPrivilege || this.loading || this.invalidForm) return
+      this.loading = true
       const data = {
         name: this.newDataset.name.trim(),
         description: this.newDataset.description.trim()
       }
-      if (!isEmpty(this.setList)) data.sets = this.setList
+      // Add list of set names to the payload when creating a new dataset
+      if (!this.datasetInstance) {
+        const setList = this.newDataset.sets.map(item => item.trim()).filter(item => item.length > 0)
+        if (!isEmpty(setList)) data.set_names = setList
+      }
       try {
         if (this.datasetInstance) {
           await this.updateCorpusDataset(
@@ -195,7 +193,7 @@ export default {
         } else {
           await this.createCorpusDataset(this.corpusId, data)
         }
-        this.createLoading = false
+        this.loading = false
         // Sending a custom event to let the parent know that it must reload the list of datasets
         this.$emit('dataset-action')
         // Close the modal
@@ -203,9 +201,9 @@ export default {
       } catch (e) {
         if (e.response?.status === 400 && e.response.data) {
           this.fieldErrors = this.parseFieldErrors(e.response.data)
-        }
+        } else this.notify({ type: 'error', text: errorParser(err) })
       } finally {
-        this.createLoading = false
+        this.loading = false
       }
     },
     parseFieldErrors (errors) {
@@ -215,6 +213,17 @@ export default {
           .entries(errors)
           .map(([key, value]) => [key, this.parseFieldErrors(value)])
       )
+    },
+    addSetField () {
+      this.newDataset.sets.push('')
+    },
+    updateSetNames (i, newValue) {
+      if (this.newDataset.sets[i] === newValue) return
+      // Do not keep the last valid value in the list if the field is emptied
+      if (!String(newValue).length) {
+        (this.newDataset.sets.splice(i, 1, newValue))
+      }
+      this.newDataset.sets.splice(i, 1, newValue)
     }
   },
   watch: {
@@ -225,13 +234,13 @@ export default {
           this.newDataset = {
             name: this.datasetInstance.name,
             description: this.datasetInstance.description,
-            sets: this.datasetInstance.sets.join(', ')
+            sets: this.datasetInstance.sets
           }
         } else {
           this.newDataset = {
             name: '',
             description: '',
-            sets: ''
+            sets: ['train', 'validation', 'test']
           }
         }
         this.fieldErrors = {}
@@ -240,7 +249,3 @@ export default {
   }
 }
 </script>
-
-<style lang="scss" scoped>
-.field-label { min-width: 11ch }
-</style>
diff --git a/src/components/Element/Datasets/ElementDatasets.vue b/src/components/Element/Datasets/ElementDatasets.vue
index 195843b284039c3be1334859c81bae69f30fddde..ea7ff5b29a95234b1614a767cdd58c9cbdcd5159 100644
--- a/src/components/Element/Datasets/ElementDatasets.vue
+++ b/src/components/Element/Datasets/ElementDatasets.vue
@@ -1,6 +1,6 @@
 <template>
-  <div class="loader m-auto is-size-3" v-if="datasets === null"></div>
-  <div class="message-body has-text-grey" v-else-if="datasets.length === 0">
+  <div class="loader m-auto is-size-3" v-if="datasetSets === null"></div>
+  <div class="message-body has-text-grey" v-else-if="datasetSets.length === 0">
     No datasets
   </div>
   <table v-else class="table is-fullwidth is-hoverable">
@@ -11,10 +11,10 @@
       <th></th>
     </thead>
     <tbody>
-      <tr v-for="elementdataset in datasets" :key="elementdataset.dataset.id + elementdataset.set">
+      <tr v-for="elementset in datasetSets" :key="elementset.dataset.id + elementset.set">
         <Row
           :corpus-id="corpusId"
-          :element-dataset="elementdataset"
+          :element-set="elementset"
           :element="element"
         />
       </tr>
@@ -24,7 +24,7 @@
 
 <script lang="ts">
 import { defineComponent, PropType } from 'vue'
-import { ElementDataset } from '@/types/dataset'
+import { ElementDatasetSet } from '@/types/dataset'
 import { mapState } from 'pinia'
 import { useElementsStore } from '@/stores'
 import { Element } from '@/types'
@@ -45,9 +45,9 @@ export default defineComponent({
     }
   },
   computed: {
-    ...mapState(useElementsStore, ['elementDatasets']),
-    datasets (): ElementDataset[] | null {
-      return this.elementDatasets?.[this.element.id] ?? null
+    ...mapState(useElementsStore, ['elementDatasetSets']),
+    datasetSets (): ElementDatasetSet[] | null {
+      return this.elementDatasetSets?.[this.element.id] ?? null
     }
   }
 })
diff --git a/src/components/Element/Datasets/Row.vue b/src/components/Element/Datasets/Row.vue
index 0285d344b5a9c6f0d5a4c0636b529f249a81361c..174c479a70139dfa1d63ef24df72b331bdbe9773 100644
--- a/src/components/Element/Datasets/Row.vue
+++ b/src/components/Element/Datasets/Row.vue
@@ -1,23 +1,23 @@
 <template>
   <td>
-    <router-link :to="{ name: 'dataset-details', params: { datasetId: elementDataset.dataset.id } }" title="Dataset details">
-      {{ elementDataset.dataset.name }}
+    <router-link :to="{ name: 'dataset-details', params: { datasetId: elementSet.dataset.id } }" title="Dataset details">
+      {{ elementSet.dataset.name }}
     </router-link>
   </td>
-  <td>{{ elementDataset.set }}</td>
+  <td>{{ elementSet.set }}</td>
   <td class="shrink has-text-right">
     <component
-      :is="elementDataset.previous ? 'router-link' : 'span'"
-      :class="!elementDataset.previous ? 'has-text-grey' : ''"
-      :to="elementDataset.previous ? { name: 'element-details', params: { id: elementDataset.previous } } : ''"
+      :is="elementSet.previous ? 'router-link' : 'span'"
+      :class="!elementSet.previous ? 'has-text-grey' : ''"
+      :to="elementSet.previous ? { name: 'element-details', params: { id: elementSet.previous } } : ''"
       title="Go to the previous element in this dataset and set"
     >
       <i class="icon-arrow-left"></i>
     </component>
     <component
-      :is="elementDataset.next ? 'router-link' : 'span'"
-      :class="!elementDataset.next ? 'has-text-grey' : ''"
-      :to="elementDataset.next ? { name: 'element-details', params: { id: elementDataset.next } } : ''"
+      :is="elementSet.next ? 'router-link' : 'span'"
+      :class="!elementSet.next ? 'has-text-grey' : ''"
+      :to="elementSet.next ? { name: 'element-details', params: { id: elementSet.next } } : ''"
       title="Go to the next element in this dataset and set"
     >
       <i class="icon-arrow-right"></i>
@@ -33,10 +33,10 @@
     </a>
   </td>
   <RemoveModal
-    v-if="elementDataset.dataset && elementDataset.set"
+    v-if="elementSet.dataset && elementSet.set"
     v-model="removeElementModal"
-    :dataset="elementDataset.dataset"
-    :set="elementDataset.set"
+    :dataset="elementSet.dataset"
+    :set="elementSet.set"
     :element="element"
   />
 </template>
@@ -44,7 +44,7 @@
 <script lang="ts">
 import { corporaMixin } from '@/mixins'
 import { defineComponent, PropType } from 'vue'
-import { ElementDataset } from '@/types/dataset'
+import { ElementDatasetSet } from '@/types/dataset'
 import { mapState } from 'pinia'
 import { useElementsStore } from '@/stores'
 import RemoveModal from '@/components/Dataset/RemoveModal.vue'
@@ -66,8 +66,8 @@ export default defineComponent({
       type: Object as PropType<Element>,
       required: true
     },
-    elementDataset: {
-      type: Object as PropType<ElementDataset>,
+    elementSet: {
+      type: Object as PropType<ElementDatasetSet>,
       required: true
     }
   },
@@ -75,17 +75,17 @@ export default defineComponent({
     removeElementModal: false
   }),
   computed: {
-    ...mapState(useElementsStore, ['elementDatasets']),
-    datasets (): ElementDataset[] | null {
-      return this.elementDatasets?.[this.element.id] ?? null
+    ...mapState(useElementsStore, ['elementDatasetSets']),
+    datasets (): ElementDatasetSet[] | null {
+      return this.elementDatasetSets?.[this.element.id] ?? null
     },
     canRemove () {
-      return (this.elementDataset.dataset && this.elementDataset.dataset.state === 'open' && this.elementDataset.set && this.canWrite(this.corpus))
+      return (this.elementSet.dataset && this.elementSet.dataset.state === 'open' && this.elementSet.set && this.canWrite(this.corpus))
     },
     title () {
       if (!this.canWrite(this.corpus)) return 'You do not have the required rights to edit this dataset'
-      else if (this.elementDataset.dataset.state !== 'open') return 'Only open datasets can be edited'
-      else return 'Remove element from this dataset'
+      else if (this.elementSet.dataset.state !== 'open') return 'Only open datasets can be edited'
+      else return 'Remove element from this dataset set'
     }
   },
   methods: {
diff --git a/src/components/Element/DetailsPanel.vue b/src/components/Element/DetailsPanel.vue
index 298ed37400aad7018abfdde06cb345e34059f259..556f95366098094b2063230db18bf1336fff4f89 100644
--- a/src/components/Element/DetailsPanel.vue
+++ b/src/components/Element/DetailsPanel.vue
@@ -80,7 +80,7 @@ import { corporaMixin } from '@/mixins'
 import { defineComponent, PropType } from 'vue'
 import { UUID_REGEX } from '@/config'
 import { UUID, Element } from '@/types'
-import { ElementDataset } from '@/types/dataset'
+import { ElementDatasetSet } from '@/types/dataset'
 import { mapState, mapActions } from 'pinia'
 import { useElementsStore } from '@/stores'
 
@@ -128,7 +128,7 @@ export default defineComponent({
   computed: {
     ...mapVuexState('elements', ['elements']),
     ...mapVuexState('classification', ['hasMLClasses']),
-    ...mapState(useElementsStore, ['elementDatasets']),
+    ...mapState(useElementsStore, ['elementDatasetSets']),
     ...mapVuexGetters('elements', {
       // canWrite and canAdmin are already defined in corporaMixin
       canWriteElement: 'canWrite',
@@ -160,14 +160,14 @@ export default defineComponent({
       // @ts-expect-error Some Element attributes like metadata are set on the fly on the former store
       return (this.element && this.element?.metadata) || []
     },
-    datasets (): ElementDataset[] | null {
-      return this.elementDatasets?.[this.elementId] ?? null
+    datasets (): ElementDatasetSet[] | null {
+      return this.elementDatasetSets?.[this.elementId] ?? null
     }
   },
   methods: {
     ...mapVuexActions('classification', { classificationCreate: 'create' }),
     ...mapVuexActions('elements', { retrieveElement: 'get' }),
-    ...mapActions(useElementsStore, ['listElementDatasets']),
+    ...mapActions(useElementsStore, ['listElementDatasetSets']),
     async createClassification () {
       if (!this.canCreateClassification) return
       this.isSavingNewClassification = true
@@ -195,7 +195,7 @@ export default defineComponent({
          * or some element attributes are not displayed at all.
          */
         if (!this.element || this.element.id !== id || !this.element.rights || !this.element.classifications) this.retrieveElement({ id })
-        if (!Array.isArray(this.elementDatasets[id])) this.listElementDatasets(id, { with_neighbors: true })
+        if (!Array.isArray(this.elementDatasetSets[id])) this.listElementDatasetSets(id, { with_neighbors: true })
       }
     },
     selectedNewClassification () {
diff --git a/src/components/Navigation/DatasetFromSelectionModal.vue b/src/components/Navigation/DatasetFromSelectionModal.vue
index d8502a21b488ce8ea95074e0b7fe304985b4fd0c..f009355fb43af266249607ebc83fbca0be9d4dec 100644
--- a/src/components/Navigation/DatasetFromSelectionModal.vue
+++ b/src/components/Navigation/DatasetFromSelectionModal.vue
@@ -55,7 +55,7 @@
             <div class="control">
               <span class="select is-fullwidth">
                 <select
-                  v-model="set"
+                  v-model="selectedSet"
                   :disabled="(!selectedDataset || loading) ?? null"
                   :title="selectedDataset ? `Set elements will be added to on dataset ${selectedDataset.name}` : 'Please select a dataset first' "
                 >
@@ -64,9 +64,9 @@
                     <option
                       v-for="value in selectedDataset.sets"
                       :value="value"
-                      :key="value"
+                      :key="value.id"
                     >
-                      {{ value }}
+                      {{ value.name }}
                     </option>
                   </template>
                 </select>
@@ -98,7 +98,7 @@ import { mapState, mapActions } from 'pinia'
 import { corporaMixin, truncateMixin } from '@/mixins'
 import { UUID_REGEX } from '@/config'
 import { UUID } from '@/types'
-import { Dataset } from '@/types/dataset'
+import { Dataset, DatasetSet } from '@/types/dataset'
 import Modal from '@/components/Modal.vue'
 import { defineComponent, PropType } from 'vue'
 import { useDatasetStore } from '@/stores'
@@ -137,7 +137,7 @@ export default defineComponent({
     /**
      * Name of the set elements will be added to
      */
-    set: null as string | null
+    selectedSet: null as DatasetSet | null
   }),
   computed: {
     ...mapState(useDatasetStore, ['corpusDatasets', 'singleCorpusDatasets']),
@@ -145,7 +145,7 @@ export default defineComponent({
       return `Add ${this.corpus?.name} selection to a dataset`
     },
     canAdd (): boolean {
-      return this.selectedDataset !== null && this.set !== null
+      return this.selectedDataset !== null && this.selectedSet !== null
     },
     availableDatasets (): Dataset[] | null {
       if (this.corpusDatasets[this.corpusId] === undefined) return null
@@ -157,19 +157,17 @@ export default defineComponent({
     updateModelValue (value: boolean) { this.$emit('update:modelValue', value) },
     async addToSelection () {
       if (!this.canAdd) return
-      if (!this.selectedDataset || !this.set) return
+      if (!this.selectedDataset || !this.selectedSet) return
 
       this.loading = true
       try {
         await this.addDatasetElementsSelection(
           this.corpusId,
-          {
-            dataset_id: this.selectedDataset.id,
-            set: this.set
-          }
+          this.selectedDataset.id,
+          this.selectedSet
         )
         this.selectedDataset = null
-        this.set = null
+        this.selectedSet = null
         this.$emit('update:modelValue', false)
       } finally {
         this.loading = false
diff --git a/src/components/Navigation/ElementList.vue b/src/components/Navigation/ElementList.vue
index 618c80b61cf8fc48a2b536ebfa831822b4ea9378..cf8778824d3316b75bde9d141f046ea7d8cef80b 100644
--- a/src/components/Navigation/ElementList.vue
+++ b/src/components/Navigation/ElementList.vue
@@ -52,7 +52,7 @@ import ElementThumbnail from './ElementThumbnail.vue'
 import ElementInLine from './ElementInLine.vue'
 import { ElementList } from '@/types'
 import { ProcessElementList } from '@/types/process'
-import { Dataset } from '@/types/dataset'
+import { Dataset, DatasetSet } from '@/types/dataset'
 
 function isElementList (element: ElementList | ProcessElementList): element is ElementList {
   return 'type' in element
@@ -101,7 +101,7 @@ export default defineComponent({
       default: null
     },
     set: {
-      type: String,
+      type: Object as PropType<DatasetSet | null>,
       default: null
     }
   },
diff --git a/src/components/Navigation/ElementThumbnail.vue b/src/components/Navigation/ElementThumbnail.vue
index ad4ed1c8ba5680492f8f0b978330ac27a3fee58d..24ce01f0ef20edd98208a9bee90a5093607e0e9d 100644
--- a/src/components/Navigation/ElementThumbnail.vue
+++ b/src/components/Navigation/ElementThumbnail.vue
@@ -69,10 +69,10 @@
         <i class="icon-remove-square"></i>
       </button>
       <RemoveModal
-        v-if="dataset && dataset.state === 'open'"
+        v-if="dataset && set && dataset.state === 'open'"
         v-model="removeElementModal"
         :dataset="dataset"
-        :set="set"
+        :set="set.name"
         :element="element"
       />
       <button
@@ -104,7 +104,7 @@ import { useDisplayStore } from '@/stores'
 
 import DeleteModal from '@/components/Element/DeleteModal.vue'
 import PreviewDropdown from './PreviewDropdown.vue'
-import { Dataset } from '@/types/dataset'
+import { Dataset, DatasetSet } from '@/types/dataset'
 import { ElementList } from '@/types'
 import RemoveModal from '@/components/Dataset/RemoveModal.vue'
 
@@ -132,7 +132,7 @@ export default defineComponent({
       default: null
     },
     set: {
-      type: String,
+      type: Object as PropType<DatasetSet | null>,
       default: null
     }
   },
diff --git a/src/components/Process/Datasets/AddForm.vue b/src/components/Process/Datasets/AddForm.vue
index a9e8113f97c3df86139e1ce35364546f3d52a1f7..10fda7602dddc92a1982637422b27dc5c4db6d23 100644
--- a/src/components/Process/Datasets/AddForm.vue
+++ b/src/components/Process/Datasets/AddForm.vue
@@ -6,7 +6,7 @@
     <td>
       <span class="select is-fullwidth">
         <select
-          form="dataset-add"
+          form="set-add"
           v-model="corpusFilter"
           :disabled="loading || null"
         >
@@ -19,17 +19,17 @@
     <td>
       <span class="select is-fullwidth">
         <select
-          form="dataset-add"
+          form="set-add"
           v-model="datasetId"
           :disabled="loading || !corpusFilter || !corpusDatasets?.length || null"
         >
           <option value="" disabled selected>&mdash;</option>
-          <!-- Display all datasets in the corpus, disabling the options for datasets that were already added to the process -->
+          <!-- Display all datasets in the corpus, disabling the option for datasets of which all sets are already added to the process -->
           <option
-            v-for="corpusDataset in singleCorpusDatasets(corpusFilter) ?? []"
+            v-for="corpusDataset in corpusDatasets ?? []"
             :key="corpusDataset.id"
             :value="corpusDataset.id"
-            :disabled="processDatasets[processId]?.some(({ dataset }) => dataset.id === corpusDataset.id) || null"
+            :disabled="allSetsSeletected(corpusDataset)"
           >
             {{ corpusDataset.name }}
           </option>
@@ -41,18 +41,25 @@
     </td>
     <template v-if="dataset">
       <td>
-        <div v-for="set in dataset.sets" :key="set">
-          <label class="checkbox">
-            <input
-              type="checkbox"
-              v-model="datasetSets[set]"
-            />
-            {{ truncateLong(set) }}
-          </label>
-        </div>
-        <template v-if="fieldErrors.sets">
-          <p v-for="(error, i) in fieldErrors.sets" :key="i" class="help is-danger">{{ error }}</p>
-        </template>
+        <span class="select is-fullwidth">
+          <select
+            form="set-add"
+            v-model="setIds"
+            :disabled="loading || !corpusFilter || !dataset?.sets.length || null"
+          >
+            <option :value="[]" disabled selected>&mdash;</option>
+            <option v-if="remainingSets(dataset).length > 1" :value="remainingSets(dataset)" :disabled="allSetsSeletected(dataset)">All sets</option>
+            <!-- Display all sets in the dataset, disabling the option for sets that are already added to the process -->
+            <option
+              v-for="datasetSet in dataset.sets ?? []"
+              :key="datasetSet.id"
+              :value="[datasetSet.id]"
+              :disabled="processSets[processId]?.some(( item ) => item.dataset.id === datasetId && item.set_name === datasetSet.name) || null"
+            >
+              {{ datasetSet.name }}
+            </option>
+          </select>
+        </span>
       </td>
       <td>
         <StateTag :state="dataset.state" />
@@ -61,7 +68,7 @@
     <td :colspan="dataset ? 1 : 3" class="shrink has-text-right">
       <button
         type="submit"
-        form="dataset-add"
+        form="set-add"
         class="button is-primary"
         :disabled="!canAdd || null"
         :title="addButtonTitle"
@@ -73,7 +80,7 @@
       The <select> and <button> are linked to this form by its ID, to allow pressing Enter anywhere to add the dataset.
       Putting a <form> inside of a <tr> or <td> is forbidden, so it is placed here and will be invisible.
     -->
-    <form id="dataset-add" v-on:submit.prevent="add"></form>
+    <form id="set-add" v-on:submit.prevent="add"></form>
   </tr>
 </template>
 
@@ -106,12 +113,12 @@ export default {
     loading: false,
     corpusFilter: '',
     datasetId: '',
-    datasetSets: {},
+    setIds: [],
     fieldErrors: {}
   }),
   computed: {
     ...mapState(useDatasetStore, ['singleCorpusDatasets']),
-    ...mapVuexState('process', ['processes', 'processDatasets']),
+    ...mapVuexState('process', ['processes', 'processSets']),
     process () {
       return this.processes[this.processId]
     },
@@ -126,49 +133,44 @@ export default {
       return this.corpusDatasets?.find(({ id }) => id === this.datasetId)
     },
     canAdd () {
-      return !this.loading && !this.process.started && this.canAdmin(this.corpus) && this.dataset
+      return !this.loading && !this.process.started && this.canAdmin(this.corpus) && this.setIds.length
     },
     addButtonTitle () {
       if (this.loading) return 'Loading…'
       if (this.process.started) return 'This process has already started and cannot be modified'
       if (!this.canAdmin(this.corpus)) return "You must have an admin access to the process' project to add a dataset"
-      if (!this.dataset) return 'You must select a dataset to add'
-      return 'Add a dataset to this process'
+      if (!this.setIds.length) return 'You must select a dataset set to add'
+      return 'Add a dataset set to this process'
     }
   },
   methods: {
     ...mapActions(useDatasetStore, ['listCorpusDatasets']),
-    ...mapVuexActions('process', ['createProcessDataset']),
+    ...mapVuexActions('process', ['createProcessSet']),
     ...mapActions(useNotificationStore, ['notify']),
+    allSetsSeletected (corpusDataset) {
+      return this.processSets[this.processId]?.filter((item) => item.dataset.id === corpusDataset.id).length === corpusDataset.sets.length
+    },
+    remainingSets (dataset) {
+      const selectedSets = this.processSets[this.processId]?.filter((item) => item.dataset.id === dataset.id).map((item) => item.set_name) ?? []
+      return dataset.sets.filter((item) => !selectedSets.includes(item.name)).map((item) => item.id)
+    },
     async add () {
       if (!this.canAdd) return
       this.loading = true
-
-      const sets = []
-      Object.keys(this.datasetSets).forEach((set) => {
-        if (this.datasetSets[set]) sets.push(set)
-      })
-      // It is not possible to select no sets at all
-      if (!sets.length > 0) {
-        this.fieldErrors = {
-          ...this.fieldErrors,
-          sets: ['At least one set must be selected.']
-        }
-        this.loading = false
-        return
-      }
-
-      try {
-        await this.createProcessDataset({ processId: this.processId, dataset: this.dataset, sets })
-        this.datasetId = ''
-      } catch (err) {
-        if (isAxiosError(err) && err.response?.status === 400 && err.response.data) {
-          this.fieldErrors = err.response.data
+      for (const setId of this.setIds) {
+        try {
+          await this.createProcessSet({ processId: this.processId, setId })
+        } catch (err) {
+          if (isAxiosError(err) && err.response?.status === 400 && err.response.data) {
+            this.fieldErrors = err.response.data
+          }
+          this.notify({ type: 'error', text: errorParser(err) })
         }
-        this.notify({ type: 'error', text: errorParser(err) })
-      } finally {
-        this.loading = false
       }
+      // Reset the dataset filter if the 'all-sets' option has been used, as it is now disabled
+      if (this.setIds.length > 1) this.datasetId = ''
+      this.setIds = []
+      this.loading = false
     }
   },
   watch: {
@@ -194,8 +196,8 @@ export default {
     datasetId (newValue, oldValue) {
       if (!this.dataset) return
       if (oldValue !== newValue) {
-        this.datasetSets = Object.fromEntries(this.dataset.sets.map(set => { return [[set], true] }))
         this.fieldErrors = {}
+        this.setIds = []
       }
     }
   }
diff --git a/src/components/Process/Datasets/Row.vue b/src/components/Process/Datasets/Row.vue
index 7b55378e769228acfb14c826d62e6962cbc138fa..ea9df78dca6cb6ed5be1e226e5dd555bc928e4c9 100644
--- a/src/components/Process/Datasets/Row.vue
+++ b/src/components/Process/Datasets/Row.vue
@@ -1,33 +1,19 @@
 <template>
   <tr>
     <td class="shrink">
-      <ItemId :item-id="processDataset.dataset.id" />
+      <ItemId :item-id="processSet.id" />
     </td>
     <td>
       {{ corpus.name }}
     </td>
     <td>
-      {{ processDataset.dataset.name }}
+      {{ processSet.dataset.name }}
     </td>
     <td>
-      <div v-for="set in processDataset.dataset.sets" :key="set">
-        <label class="checkbox">
-          <input
-            :disabled="loading || lastSet(set)"
-            type="checkbox"
-            :checked="processDataset.sets.includes(set)"
-            v-on:change="updateSets(set, $event.target.checked)"
-            :title="lastSet(set) ? 'At least one set must be selected' : ''"
-          />
-          {{ truncateLong(set) }}
-        </label>
-      </div>
-      <template v-if="setErrors.length">
-        <p v-for="(error, i) in setErrors" :key="i" class="help is-danger">{{ error }}</p>
-      </template>
+      {{ processSet.set_name }}
     </td>
     <td>
-      <StateTag :state="processDataset.dataset.state" />
+      <StateTag :state="processSet.dataset.state" />
     </td>
     <td v-if="!readOnly" class="shrink has-text-right">
       <button
@@ -49,7 +35,6 @@ import { mapActions } from 'pinia'
 import { corporaMixin, truncateMixin } from '@/mixins'
 import ItemId from '@/components/ItemId.vue'
 import StateTag from '@/components/Corpus/Datasets/StateTag.vue'
-import { errorParser } from '@/helpers'
 import { useNotificationStore } from '@/stores'
 
 export default {
@@ -62,7 +47,7 @@ export default {
     StateTag
   },
   props: {
-    processDataset: {
+    processSet: {
       type: Object,
       required: true
     },
@@ -85,7 +70,7 @@ export default {
       return this.processes[this.processId] ?? {}
     },
     corpusId () {
-      return this.processDataset.dataset.corpus_id
+      return this.processSet.dataset.corpus_id
     },
     canRemove () {
       return !this.loading && this.process?.corpus && this.canAdmin(this.corpora[this.process.corpus]) && !this.process?.started
@@ -98,61 +83,26 @@ export default {
     }
   },
   methods: {
-    ...mapVuexActions('process', ['deleteProcessDataset', 'updateProcessDataset']),
+    ...mapVuexActions('process', ['deleteProcessSet']),
     ...mapActions(useNotificationStore, ['notify']),
     async remove () {
       if (!this.canRemove) return
       this.loading = true
+      const setId = this.processSet.dataset.sets.find((item) => item.name === this.processSet.set_name).id
       try {
-        await this.deleteProcessDataset({
+        await this.deleteProcessSet({
           processId: this.processId,
-          datasetId: this.processDataset.dataset.id,
-          processDatasetId: this.processDataset.id
+          setId,
+          processSetId: this.processSet.id
         })
       } catch (err) {
         /*
          * Only remove the loading state when the deletion fails, not all the time.
-         * This gives time for the subsequent ListProcessDatasets to refresh the datasets and hide this deleted row
+         * This gives time for the subsequent listProcessSets to refresh the datasets and hide this deleted row
          * after the deletion is successful, without allowing the user to click the remove button again.
          */
         this.loading = false
       }
-    },
-    lastSet (set) {
-      // This set is the only one selected from the list; it cannot be unselected
-      return (this.processDataset.sets.length === 1 & this.processDataset.sets.includes(set))
-    },
-    async updateSets (set, checked) {
-      if (this.loading) return
-      this.loading = true
-      this.setErrors = []
-
-      // Copy the current process dataset sets
-      const sets = [...this.processDataset.sets]
-      // If the set is in the process dataset sets, and the checkbox is unchecked, remove it
-      const index = sets.indexOf(set)
-      if (index > -1 & !checked) sets.splice(index, 1)
-      // If the set is not in the process dataset sets, and the checkbox is checked, add it
-      else if (index === -1 & checked) sets.push(set)
-
-      // It is not possible to select no sets at all
-      if (!sets.length > 0) {
-        this.setErrors.push('At least one set must be selected.')
-        this.loading = false
-        return
-      }
-
-      try {
-        await this.updateProcessDataset({
-          processId: this.processId,
-          datasetId: this.processDataset.dataset.id,
-          sets
-        })
-      } catch (err) {
-        this.notify({ type: 'error', text: errorParser(err) })
-      } finally {
-        this.loading = false
-      }
     }
   }
 }
diff --git a/src/store/process.js b/src/store/process.js
index 19c63839d67dd8fa8ae46c8f3db24996d6f65d4b..40d88093f10bfc94e4db5fd2aeb4cdfac539508d 100644
--- a/src/store/process.js
+++ b/src/store/process.js
@@ -14,10 +14,10 @@ export const initialState = () => ({
   // { [processId]: ElementsPaginatedResponse }
   processElementsPage: {},
   /**
-   * ProcessDatasets by process ID.
-   * @type {{ [processId: string]: import('@/types').ProcessDataset[] }}
+   * ProcessSets by process ID.
+   * @type {{ [processId: string]: import('@/types').ProcessSet[] }}
    */
-  processDatasets: {},
+  processSets: {},
   /**
    * Tasks for the current process, with an added `timeoutId` property for task polling
    */
@@ -132,38 +132,27 @@ export const mutations = {
     state.processElementsPage = { ...state.processElementsPage, [processId]: newResponse }
   },
 
-  setProcessDatasets (state, { processId, results }) {
-    const processDatasetList = state.processDatasets[processId] || []
-    results.forEach(newProcessDataset => {
+  setProcessSets (state, { processId, results }) {
+    const processSetList = state.processSets[processId] || []
+    results.forEach(newProcessSet => {
       // Prevent duplicating datasets
-      if (!processDatasetList.some(processDataset => processDataset.id === newProcessDataset.id)) processDatasetList.push(newProcessDataset)
+      if (!processSetList.some(processSet => processSet.id === newProcessSet.id)) processSetList.push(newProcessSet)
     })
     // Merge process datasets
-    state.processDatasets = {
-      ...state.processDatasets,
-      [processId]: processDatasetList
+    state.processSets = {
+      ...state.processSets,
+      [processId]: processSetList
     }
   },
 
-  updateSingleProcessDataset (state, { processId, data }) {
-    const processDatasetList = state.processDatasets[processId] || []
-    const index = processDatasetList.findIndex(({ id }) => id === data.id)
+  removeProcessSet (state, { processId, processSetId }) {
+    const processSetList = state.processSets[processId] || []
+    const index = processSetList.findIndex(({ id }) => id === processSetId)
     if (index < 0) return
-    processDatasetList.splice(index, 1, data)
-    state.processDatasets = {
-      ...state.processDatasets,
-      [processId]: processDatasetList
-    }
-  },
-
-  removeProcessDataset (state, { processId, processDatasetId }) {
-    const processDatasetList = state.processDatasets[processId] || []
-    const index = processDatasetList.findIndex(({ id }) => id === processDatasetId)
-    if (index < 0) return
-    processDatasetList.splice(index, 1)
-    state.processDatasets = {
-      ...state.processDatasets,
-      [processId]: processDatasetList
+    processSetList.splice(index, 1)
+    state.processSets = {
+      ...state.processSets,
+      [processId]: processSetList
     }
   },
 
@@ -360,38 +349,39 @@ export const actions = {
     commit('setProcessElementsPage', { processId, response })
   },
 
-  async listProcessDatasets ({ state, commit, dispatch }, { processId, page = 1 }) {
+  async listProcessSets ({ state, commit, dispatch }, { processId, page = 1 }) {
     // Do not start fetching process datasets if they have been retrieved already
-    if (page === 1 && state.processDatasets[processId]) return
+    if (page === 1 && state.processSets[processId]) return
 
-    const data = await api.listProcessDatasets({ processId, page })
+    let data = null
+    try {
+      data = await api.listProcessSets({ processId, page })
+    } catch (err) {
+      commit('notifications/notify', { type: 'error', text: errorParser(err) }, { root: true })
+      throw err
+    }
 
     if (!data || !data.number || page !== data.number) {
       // Avoid any loop
-      throw new Error(`Pagination failed while listing datasets for process "${processId}"`)
+      throw new Error(`Pagination failed while listing sets for process "${processId}"`)
     }
 
-    commit('setProcessDatasets', { processId, results: data.results })
+    commit('setProcessSets', { processId, results: data.results })
 
     // Load other pages
-    if (data.next) await dispatch('listProcessDatasets', { processId, page: page + 1 })
+    if (data.next) await dispatch('listProcessSets', { processId, page: page + 1 })
   },
 
-  async createProcessDataset ({ commit }, { processId, dataset, sets }) {
+  async createProcessSet ({ commit }, { processId, setId }) {
     // Errors are handled in components/Process/Datasets/AddForm.vue
-    const resp = await api.createProcessDataset(processId, dataset.id, { sets })
-    commit('setProcessDatasets', { processId, results: [resp.data] })
-  },
-
-  async updateProcessDataset ({ commit }, { processId, datasetId, sets }) {
-    const resp = await api.updateProcessDataset(processId, datasetId, { sets })
-    commit('updateSingleProcessDataset', { processId, data: resp.data })
+    const resp = await api.createProcessSet(processId, setId)
+    commit('setProcessSets', { processId, results: [resp.data] })
   },
 
-  async deleteProcessDataset ({ commit }, { processId, datasetId, processDatasetId }) {
+  async deleteProcessSet ({ commit }, { processId, processSetId, setId }) {
     try {
-      await api.deleteProcessDataset(processId, datasetId)
-      commit('removeProcessDataset', { processId, processDatasetId })
+      await api.deleteProcessSet(processId, setId)
+      commit('removeProcessSet', { processId, processSetId })
     } catch (err) {
       commit('notifications/notify', { type: 'error', text: errorParser(err) }, { root: true })
       throw err
diff --git a/src/stores/dataset.ts b/src/stores/dataset.ts
index c66384894467abf1a2425cdf67250fd4235980f4..81b769487bc4e340abc9a6c1b943d001bc753c21 100644
--- a/src/stores/dataset.ts
+++ b/src/stores/dataset.ts
@@ -1,20 +1,22 @@
 import { UUID } from '@/types'
-import { Dataset, DatasetElementList } from '@/types/dataset'
+import { Dataset, DatasetElementList, DatasetSet } from '@/types/dataset'
 import { defineStore } from 'pinia'
 import { useNotificationStore } from './notification'
 import {
-  CreateDatasetElementsSelectionParameters,
   DatasetCreate,
   DatasetEdit,
   cloneDataset,
   createDataset,
   createDatasetElementsSelection,
+  createDatasetSet,
   deleteDataset,
   deleteDatasetElement,
+  deleteDatasetSet,
   listCorpusDataset,
   listDatasetElements,
   retrieveDataset,
-  updateDataset
+  updateDataset,
+  updateDatasetSet
 } from '@/api'
 import { errorParser } from '@/helpers'
 import { useElementsStore } from './elements'
@@ -33,7 +35,7 @@ interface State {
    */
   datasetElementPagination: {
     [datasetId: UUID]: {
-      [set: string]: { datasetElements: DatasetElementList[], nextCursor: string | null }
+      [setName: string]: { datasetElements: DatasetElementList[], nextCursor: string | null }
     }
   }
 }
@@ -127,12 +129,12 @@ export const useDatasetStore = defineStore('dataset', {
     },
 
     // Add selected elements to a dataset of the same corpus
-    async addDatasetElementsSelection (corpusId: UUID, params: CreateDatasetElementsSelectionParameters) {
+    async addDatasetElementsSelection (corpusId: UUID, datasetId: UUID, datasetSet: DatasetSet) {
       try {
-        await createDatasetElementsSelection(corpusId, params)
+        await createDatasetElementsSelection(corpusId, datasetSet.id)
         // Reset an eventual cursor pagination listing elements of this set
-        delete this.datasetElementPagination[params.dataset_id]?.[params.set]
-        useNotificationStore().notify({ type: 'success', text: 'Elements have been added to the dataset.' })
+        delete this.datasetElementPagination[datasetId]?.[datasetSet.name]
+        useNotificationStore().notify({ type: 'success', text: 'Elements have been added to the dataset set.' })
       } catch (err) {
         useNotificationStore().notify({ type: 'error', text: errorParser(err) })
         throw err
@@ -145,34 +147,34 @@ export const useDatasetStore = defineStore('dataset', {
      * If no pagination was stored in the state, starts from the first page.
      * If a pagination existed and a next page is available, resumes paginating.
      */
-    async nextDatasetElements (datasetId: UUID, set: string) {
+    async nextDatasetElements (datasetId: UUID, setName: string) {
       if (this.datasetElementPagination[datasetId] === undefined) this.datasetElementPagination[datasetId] = {}
-      const currentPage = this.datasetElementPagination[datasetId][set]
+      const currentPage = this.datasetElementPagination[datasetId][setName]
       if (currentPage && currentPage.nextCursor === null) {
         useNotificationStore().notify({ type: 'warning', text: 'All elements have been fetched already.' })
         return
       }
-      // Return early when we know no element are part of this set
-      if (this.datasets[datasetId]?.set_elements?.[set] === 0) {
-        this.datasetElementPagination[datasetId][set] = { datasetElements: [], nextCursor: null }
+      // Return early when we know no element is part of this set
+      if (this.datasets[datasetId]?.set_elements?.[setName] === 0) {
+        this.datasetElementPagination[datasetId][setName] = { datasetElements: [], nextCursor: null }
         return
       }
       try {
-        const resp = await listDatasetElements(datasetId, { set, cursor: currentPage?.nextCursor ?? null })
+        const resp = await listDatasetElements(datasetId, { set: setName, cursor: currentPage?.nextCursor ?? null })
         const nextCursor = resp.next && new URL(resp.next).searchParams.get('cursor')
-        this.datasetElementPagination[datasetId][set] = { datasetElements: [...(currentPage?.datasetElements ?? []), ...resp.results], nextCursor }
+        this.datasetElementPagination[datasetId][setName] = { datasetElements: [...(currentPage?.datasetElements ?? []), ...resp.results], nextCursor }
       } catch (err) {
         useNotificationStore().notify({ type: 'error', text: errorParser(err) })
         throw err
       }
     },
 
-    async removeDatasetElement (datasetId: UUID, elementId: UUID, set: string) {
+    async removeDatasetElement (datasetId: UUID, elementId: UUID, setName: string) {
       try {
-        await deleteDatasetElement(datasetId, elementId, set)
+        await deleteDatasetElement(datasetId, elementId, setName)
         // When the action is called from the element details panel, this.datasetElementPagination[datasetId] can be undefined
         if (this.datasetElementPagination[datasetId]) {
-          const currentPage = this.datasetElementPagination[datasetId][set]
+          const currentPage = this.datasetElementPagination[datasetId][setName]
           if (currentPage) {
             const index = currentPage.datasetElements.findIndex(datasetElement => datasetElement.element.id === elementId)
             if (index >= 0) currentPage.datasetElements.splice(index, 1)
@@ -180,17 +182,52 @@ export const useDatasetStore = defineStore('dataset', {
         }
         // Update dataset sets count
         const setElements = this.datasets[datasetId]?.set_elements
-        if (setElements && setElements[set] !== undefined) setElements[set] -= 1
+        if (setElements && setElements[setName] !== undefined) setElements[setName] -= 1
         // Remove element link to the dataset
-        const elementDatasets = useElementsStore().elementDatasets[elementId]
-        if (elementDatasets === undefined) return
-        const index = elementDatasets.findIndex(dataset => dataset.dataset.id === datasetId && dataset.set === set)
-        if (index < 0) throw new Error(`Dataset ${elementId} not found in set ${set} of dataset ${datasetId}`)
-        elementDatasets.splice(index, 1)
+        const elementDatasetSets = useElementsStore().elementDatasetSets[elementId]
+        if (elementDatasetSets === undefined) return
+        const index = elementDatasetSets.findIndex(dss => dss.dataset.id === datasetId && dss.set === setName)
+        if (index < 0) throw new Error(`Element ${elementId} not found in set ${setName} of dataset ${datasetId}`)
+        elementDatasetSets.splice(index, 1)
       } catch (err) {
         useNotificationStore().notify({ type: 'error', text: errorParser(err) })
         throw err
       }
+    },
+
+    async createDatasetSet (datasetId: UUID, name: string) {
+      try {
+        const data = await createDatasetSet(datasetId, name)
+        this.datasets[datasetId].sets.push(data)
+      } catch (err) {
+        useNotificationStore().notify({ type: 'error', text: errorParser(err) })
+        throw err
+      }
+    },
+
+    async updateDatasetSet (datasetId: UUID, setId: UUID, name: string) {
+      try {
+        const data = await updateDatasetSet(datasetId, setId, name)
+        const datasetSets = this.datasets[datasetId].sets
+        const index = datasetSets.findIndex(dss => dss.id === setId)
+        if (index < 0) throw new Error(`Set ${setId} not found in dataset ${datasetId}`)
+        datasetSets[index] = data
+      } catch (err) {
+        useNotificationStore().notify({ type: 'error', text: errorParser(err) })
+        throw err
+      }
+    },
+
+    async deleteDatasetSet (datasetId: UUID, setId: UUID) {
+      try {
+        await deleteDatasetSet(datasetId, setId)
+        const datasetSets = this.datasets[datasetId].sets
+        const index = datasetSets.findIndex(dss => dss.id === setId)
+        if (index < 0) throw new Error(`Set ${setId} not found in dataset ${datasetId}`)
+        datasetSets.splice(index, 1)
+      } catch (err) {
+        useNotificationStore().notify({ type: 'error', text: errorParser(err) })
+      }
     }
   },
 
diff --git a/src/stores/elements.ts b/src/stores/elements.ts
index 5e1cc433186658addc759eacc918e42ab3735b91..9701bdb93171ef6cce6d38f63be4433f9d8a5b7f 100644
--- a/src/stores/elements.ts
+++ b/src/stores/elements.ts
@@ -2,7 +2,7 @@ import { defineStore } from 'pinia'
 import { PageNumberPaginationParameters } from '@/api'
 import { errorParser } from '@/helpers'
 import { UUID } from '@/types'
-import { ElementDataset } from '@/types/dataset'
+import { ElementDatasetSet } from '@/types/dataset'
 import { useNotificationStore } from '@/stores'
 import * as api from '@/api'
 
@@ -10,31 +10,31 @@ interface State {
   /**
    * List of dataset that contains a specific element
    */
-  elementDatasets: { [elementId: UUID]: ElementDataset[] }
+  elementDatasetSets: { [elementId: UUID]: ElementDatasetSet[] }
 }
 
-interface ListElementDatasetsParameters extends PageNumberPaginationParameters {
+interface ListElementSetsParameters extends PageNumberPaginationParameters {
   with_neighbors?: boolean
 }
 
 export const useElementsStore = defineStore('elements', {
   state: (): State => ({
-    elementDatasets: {}
+    elementDatasetSets: {}
   }),
   actions: {
-    async listElementDatasets (elementId: UUID, params: ListElementDatasetsParameters = {}) {
+    async listElementDatasetSets (elementId: UUID, params: ListElementSetsParameters = {}) {
       // Avoid listing datasets twice for an element
-      if ((!params.page || params.page === 1) && this.elementDatasets?.[elementId]) return
+      if ((!params.page || params.page === 1) && this.elementDatasetSets?.[elementId]) return
       // List datasets containing a specific element through all pages
       try {
-        const response = await api.listElementDatasets(elementId, params)
+        const response = await api.listElementDatasetSets(elementId, params)
 
         // Progressively add results to the store
-        if (elementId in this.elementDatasets) this.elementDatasets[elementId].push(...response.results)
-        else this.elementDatasets[elementId] = response.results
+        if (elementId in this.elementDatasetSets) this.elementDatasetSets[elementId].push(...response.results)
+        else this.elementDatasetSets[elementId] = response.results
 
         // Follow pagination without awaiting, until we fetched all the data
-        if (response.next !== null) this.listElementDatasets(elementId, { ...params, page: response.number + 1 })
+        if (response.next !== null) this.listElementDatasetSets(elementId, { ...params, page: response.number + 1 })
       } catch (err) {
         useNotificationStore().notify({ type: 'error', text: errorParser(err) })
         throw err
diff --git a/src/types/dataset.ts b/src/types/dataset.ts
index dbc43877c68dce9c1fa0331fed2f83532f456cfd..166b51abed2f8bd6ed6697028d72ebf283a97d78 100644
--- a/src/types/dataset.ts
+++ b/src/types/dataset.ts
@@ -3,12 +3,17 @@ import { UUID, ElementList } from '@/types'
 
 export type DatasetState = keyof typeof DATASET_STATES
 
+export interface DatasetSet {
+  id: UUID
+  name: string
+}
+
 export interface Dataset {
   id: UUID
   state: DatasetState
   name: string
   description: string
-  sets: string[]
+  sets: DatasetSet[]
   set_elements: Record<string, number> | null
   corpus_id: UUID
   creator: string
@@ -22,7 +27,7 @@ export interface DatasetElementList {
   element: ElementList
 }
 
-export interface ElementDataset {
+export interface ElementDatasetSet {
   dataset: Dataset
   set: string
   previous?: UUID | null
@@ -32,5 +37,5 @@ export interface ElementDataset {
 export interface ProcessDataset {
   id: UUID,
   dataset: Dataset,
-  sets: string[]
+  set_name: string
 }
diff --git a/src/views/Dataset/Details.vue b/src/views/Dataset/Details.vue
index 0e3829443d594a27ca75a39f9979155d020564c4..88f9b13b97a14cc289b553e6e1cad14c05c37681 100644
--- a/src/views/Dataset/Details.vue
+++ b/src/views/Dataset/Details.vue
@@ -35,13 +35,13 @@
         <ul>
           <li
             v-for="set in dataset.sets"
-            :key="set"
-            :class="set === selectedSet ? 'is-active' : ''"
+            :key="set.id"
+            :class="set.id === selectedSet?.id ? 'is-active' : ''"
           >
             <a v-on:click="selectSet(set)">
-              {{ set }}
+              {{ set.name }}
               <span class="tag is-rounded ml-1" :class="{ 'loader': dataset.set_elements === null }">
-                {{ dataset.set_elements?.[set] ?? '—' }}
+                {{ dataset.set_elements?.[set.name] ?? '—' }}
               </span>
             </a>
           </li>
@@ -50,18 +50,18 @@
 
       <KeepAlive
         v-for="set in dataset.sets"
-        :key="set"
+        :key="set.id"
       >
         <ElementList
-          v-if="set === selectedSet"
+          v-if="set.id === selectedSet?.id"
           disabled
           max-size
-          :elements="datasetSetElements(set)"
+          :elements="datasetSetElements(set.name)"
           :dataset="dataset"
           :set="set"
         >
           <template v-slot:no-results>
-            <div v-if="!elementsLoading[set]" class="notification is-warning">
+            <div v-if="!elementsLoading[set.name]" class="notification is-warning">
               <span>No elements to display.</span>
             </div>
             <div v-else>
@@ -73,11 +73,11 @@
           </template>
         </ElementList>
       </KeepAlive>
-      <div v-if="elementsLoading[selectedSet]" class="loader"></div>
+      <div v-if="selectedSet && elementsLoading[selectedSet.name]" class="loader"></div>
       <button
-        v-else-if="(datasetStats[selectedSet] ?? Infinity) > datasetSetElements(selectedSet).length"
+        v-else-if=" selectedSet && (datasetStats[selectedSet.name] ?? Infinity) > datasetSetElements(selectedSet.name).length"
         class="button"
-        v-on:click="next(selectedSet)"
+        v-on:click="next(selectedSet.name)"
       >
         Load more…
       </button>
@@ -120,6 +120,7 @@ import { useDatasetStore, useNotificationStore } from '@/stores'
 import ElementList from '@/components/Navigation/ElementList.vue'
 import StateTag from '@/components/Corpus/Datasets/StateTag.vue'
 import ItemId from '@/components/ItemId.vue'
+import { DatasetSet } from '@/types/dataset'
 
 export default defineComponent({
   mixins: [
@@ -140,7 +141,7 @@ export default defineComponent({
     }
   },
   data: () => ({
-    selectedSet: '',
+    selectedSet: null as (DatasetSet | null),
     md: new MarkdownIt({ breaks: true }),
     confirmationClone: false,
     loading: false,
@@ -149,7 +150,7 @@ export default defineComponent({
     cloneLoading: false
   }),
   mounted () {
-    Mousetrap.bind('m', () => { this.next(this.selectedSet) })
+    Mousetrap.bind('m', () => { if (this.selectedSet) this.next(this.selectedSet.name) })
   },
   beforeUnmount () {
     Mousetrap.unbind('m')
@@ -188,19 +189,19 @@ export default defineComponent({
         this.loading = false
       }
     },
-    async next (set: string) {
-      if (this.elementsLoading[set]) return
-      this.elementsLoading[set] = true
+    async next (setName: string) {
+      if (this.elementsLoading[setName]) return
+      this.elementsLoading[setName] = true
       try {
-        await this.nextDatasetElements(this.datasetId, set)
+        await this.nextDatasetElements(this.datasetId, setName)
       } catch (err) {
         this.notify({ type: 'error', text: errorParser(err) })
       } finally {
-        this.elementsLoading[set] = false
+        this.elementsLoading[setName] = false
       }
     },
-    selectSet (set: string) {
-      if (this.selectedSet === set) return
+    selectSet (set: DatasetSet) {
+      if (this.selectedSet?.id === set.id) return
       this.selectedSet = set
     },
     async clone () {
@@ -215,8 +216,8 @@ export default defineComponent({
       }
     },
     // Elements from the current pagination. Defaults to an empty list
-    datasetSetElements (set: string) {
-      return (this.datasetElementPagination[this.datasetId]?.[set]?.datasetElements ?? []).map(({ element }) => element)
+    datasetSetElements (setName: string) {
+      return (this.datasetElementPagination[this.datasetId]?.[setName]?.datasetElements ?? []).map(({ element }) => element)
     }
   },
   watch: {
@@ -230,8 +231,8 @@ export default defineComponent({
     selectedSet: {
       immediate: true,
       handler (newValue) {
-        if (!newValue || this.datasetElementPagination[this.datasetId]?.[newValue]) return
-        this.next(newValue)
+        if (!newValue || this.datasetElementPagination[this.datasetId]?.[newValue.name]) return
+        this.next(newValue.name)
       }
     }
   }
diff --git a/src/views/Process/Configure.vue b/src/views/Process/Configure.vue
index 6664410bc8aebabf07e0224fa1d07ba572c7df8b..8ec8aa89f64de36cd0db9f4e782d0cdb417191f9 100644
--- a/src/views/Process/Configure.vue
+++ b/src/views/Process/Configure.vue
@@ -310,7 +310,7 @@ export default {
     ...mapVuexGetters('auth', ['hasFeature']),
     ...mapVuexState('process', [
       'processes',
-      'processDatasets',
+      'processSets',
       'processWorkerRuns'
     ]),
     ...mapState(usePonosStore, ['farms']),
@@ -344,7 +344,7 @@ export default {
      * @type {boolean}
      */
     hasRequiredDatasets () {
-      return this.process?.mode !== 'dataset' || !Array.isArray(this.processDatasets[this.id]) || this.processDatasets[this.id].some(({ dataset }) => dataset.corpus_id === this.process.corpus)
+      return this.process?.mode !== 'dataset' || !Array.isArray(this.processSets[this.id]) || this.processSets[this.id].some(({ dataset }) => dataset.corpus_id === this.process.corpus)
     },
     canRun () {
       return this.hasWorkerRuns && this.hasRequiredDatasets
diff --git a/src/views/Process/Datasets.vue b/src/views/Process/Datasets.vue
index eb6328fbc054f21bc41427bfdd8db2d0a3aa1537..80d3c3c836fe6f4f0881238f528c2caae7a85ef4 100644
--- a/src/views/Process/Datasets.vue
+++ b/src/views/Process/Datasets.vue
@@ -2,8 +2,8 @@
   <main class="container is-fluid">
     <div v-if="processLoading" class="loading-content loader"></div>
     <template v-else-if="process.id">
-      <h1 v-if="isConfigurable" class="title">Select process datasets</h1>
-      <h1 v-else-if="isReadOnly" class="title">Process datasets</h1>
+      <h1 v-if="isConfigurable" class="title">Select process dataset sets</h1>
+      <h1 v-else-if="isReadOnly" class="title">Process dataset sets</h1>
       <h2 v-if="isReadOnly" class="subtitle">
         <div v-if="process.name">
           Process {{ process.name }}
@@ -21,16 +21,16 @@
         <tr>
           <th class="shrink">ID</th>
           <th>Project</th>
-          <th>Name</th>
-          <th>Sets</th>
-          <th>State</th>
+          <th>Dataset</th>
+          <th>Set</th>
+          <th>Dataset state</th>
           <th v-if="!isReadOnly" class="shrink">Actions</th>
         </tr>
 
-        <DatasetRow
-          v-for="dataset in datasets"
-          :key="dataset.id"
-          :process-dataset="dataset"
+        <DatasetSetRow
+          v-for="datasetSet in datasetSets"
+          :key="datasetSet.id"
+          :process-set="datasetSet"
           :process-id="id"
           :read-only="isReadOnly"
         />
@@ -41,7 +41,7 @@
       <div class="has-text-right">
         <router-link
           :class="routerLinkClass"
-          :to="datasetsLoading ? '' : { name: 'process-configure', params: { id } }"
+          :to="setsLoading ? '' : { name: 'process-configure', params: { id } }"
         >
           {{ configureButtonText }}
           <i class="icon-arrow-right"></i>
@@ -62,7 +62,7 @@ import { truncateMixin, corporaMixin } from '@/mixins'
 import { errorParser } from '@/helpers'
 
 import AddForm from '@/components/Process/Datasets/AddForm.vue'
-import DatasetRow from '@/components/Process/Datasets/Row.vue'
+import DatasetSetRow from '@/components/Process/Datasets/Row.vue'
 import ItemId from '@/components/ItemId.vue'
 
 export default {
@@ -72,7 +72,7 @@ export default {
   ],
   components: {
     AddForm,
-    DatasetRow,
+    DatasetSetRow,
     ItemId
   },
   props: {
@@ -83,7 +83,7 @@ export default {
   },
   data: () => ({
     processLoading: false,
-    datasetsLoading: false,
+    setsLoading: false,
     nameFilter: '',
     typeFilter: '',
     classFilter: ''
@@ -98,7 +98,7 @@ export default {
         /*
          * This process has started: replace this route with the process status page,
          * unless we were coming from the status page or one of the process configuration pages, or this is a dataset process,
-         * in which case we show the dataset selection page in read-only mode.
+         * in which case we show the dataset set selection page in read-only mode.
          */
         if (this.process.mode === 'dataset') {
           this.list()
@@ -113,12 +113,12 @@ export default {
     }
   },
   computed: {
-    ...mapState('process', ['processes', 'processDatasets']),
+    ...mapState('process', ['processes', 'processSets']),
     process () {
       return this.processes[this.id] ?? {}
     },
-    datasets () {
-      return this.processDatasets[this.id]
+    datasetSets () {
+      return this.processSets[this.id]
     },
     isConfigurable () {
       return this.process?.mode === 'dataset' && !this.process?.started
@@ -130,7 +130,7 @@ export default {
       return this.process.corpus
     },
     routerLinkClass () {
-      if (this.datasetsLoading) return 'button is-primary configure is-loading'
+      if (this.setsLoading) return 'button is-primary configure is-loading'
       return 'button is-primary configure'
     },
     configureButtonText () {
@@ -139,7 +139,7 @@ export default {
     }
   },
   methods: {
-    ...mapActions('process', ['retrieveProcess', 'listProcessDatasets']),
+    ...mapActions('process', ['retrieveProcess', 'listProcessSets']),
     ...mapMutations('notifications', ['notify']),
     async getProcess () {
       // Retrieve the process if it is not present nor complete in the store
@@ -154,13 +154,13 @@ export default {
       }
     },
     async list () {
-      this.datasetsLoading = true
+      this.setsLoading = true
       try {
-        this.listProcessDatasets({ processId: this.id })
+        this.listProcessSets({ processId: this.id })
       } catch (err) {
-        this.notify({ type: 'error', text: `An error occurred while listing process datasets: ${errorParser(err)}` }, { root: true })
+        this.notify({ type: 'error', text: `An error occurred while listing process sets: ${errorParser(err)}` }, { root: true })
       } finally {
-        this.datasetsLoading = false
+        this.setsLoading = false
       }
     }
   }
diff --git a/tests/unit/store/process.spec.js b/tests/unit/store/process.spec.js
index 69c30949378143431973ea54182c1a5b74d68ebb..00bd42455b97a60f786b692bec52c2744e9a41ae 100644
--- a/tests/unit/store/process.spec.js
+++ b/tests/unit/store/process.spec.js
@@ -102,52 +102,84 @@ describe('process', () => {
       })
     })
 
-    describe('setProcessDatasets', () => {
-      it('sets datasets on a process', () => {
+    describe('setProcessSets', () => {
+      it('sets dataset sets on a process', () => {
         const state = {
-          processDatasets: {
+          processSets: {
             process2: [
-              { id: 'datasetid' }
+              {
+                id: 'processsetid',
+                dataset: { id: 'datasetid' },
+                set_name: 'test'
+              }
             ]
           }
         }
-        const datasets = [
-          { id: 'dataset1' },
-          { id: 'dataset2' }
+        const processSets = [
+          {
+            id: 'processsetid2',
+            dataset: { id: 'datasetid' },
+            set_name: 'validation'
+          },
+          {
+            id: 'processsetid3',
+            dataset: { id: 'datasetid2' },
+            set_name: 'test'
+          }
         ]
-        mutations.setProcessDatasets(state, {
+        mutations.setProcessSets(state, {
           processId: 'process1',
-          results: datasets
+          results: processSets
         })
         assert.deepStrictEqual(state, {
-          processDatasets: {
-            process1: datasets,
+          processSets: {
+            process1: processSets,
             process2: [
-              { id: 'datasetid' }
+              {
+                id: 'processsetid',
+                dataset: { id: 'datasetid' },
+                set_name: 'test'
+              }
             ]
           }
         })
       })
 
-      it('adds datasets to an existing list of process datasets', () => {
+      it('adds sets to an existing list of process sets', () => {
         const state = {
-          processDatasets: {
+          processSets: {
             process1: [
-              { id: 'dataset1' }
+              {
+                id: 'processsetid',
+                dataset: { id: 'datasetid' },
+                set_name: 'test'
+              }
             ]
           }
         }
-        mutations.setProcessDatasets(state, {
+        mutations.setProcessSets(state, {
           processId: 'process1',
           results: [
-            { id: 'dataset2' }
+            {
+              id: 'processsetid2',
+              dataset: { id: 'datasetid' },
+              set_name: 'validation'
+            }
           ]
         })
         assert.deepStrictEqual(state, {
-          processDatasets: {
+          processSets: {
             process1: [
-              { id: 'dataset1' },
-              { id: 'dataset2' }
+              {
+                id: 'processsetid',
+                dataset: { id: 'datasetid' },
+                set_name: 'test'
+              },
+              {
+                id: 'processsetid2',
+                dataset: { id: 'datasetid' },
+                set_name: 'validation'
+              }
             ]
           }
         })
@@ -155,66 +187,53 @@ describe('process', () => {
 
       it('deduplicates', () => {
         const state = {
-          processDatasets: {
+          processSets: {
             process1: [
-              { id: 'dataset1' }
+              {
+                id: 'processsetid',
+                dataset: { id: 'datasetid' },
+                set_name: 'test'
+              }
             ]
           }
         }
-        mutations.setProcessDatasets(state, {
+        mutations.setProcessSets(state, {
           processId: 'process1',
           results: [
-            { id: 'dataset1' },
-            { id: 'dataset2' },
-            { id: 'dataset1' },
-            { id: 'dataset2' }
+            {
+              id: 'processsetid',
+              dataset: { id: 'datasetid' },
+              set_name: 'test'
+            },
+            {
+              id: 'processsetid2',
+              dataset: { id: 'datasetid' },
+              set_name: 'validation'
+            },
+            {
+              id: 'processsetid',
+              dataset: { id: 'datasetid' },
+              set_name: 'test'
+            },
+            {
+              id: 'processsetid2',
+              dataset: { id: 'datasetid' },
+              set_name: 'validation'
+            }
           ]
         })
         assert.deepStrictEqual(state, {
-          processDatasets: {
+          processSets: {
             process1: [
-              { id: 'dataset1' },
-              { id: 'dataset2' }
-            ]
-          }
-        })
-      })
-    })
-
-    describe('updateSingleProcessDataset', () => {
-      it('updates a single dataset on a process', () => {
-        const state = {
-          processDatasets: {
-            processid: [
               {
-                id: 'processdatasetid',
-                dataset: {
-                  id: 'datasetid'
-                },
-                sets: ['test', 'validation', 'train']
-              }
-            ]
-          }
-        }
-        mutations.updateSingleProcessDataset(state, {
-          processId: 'processid',
-          data: {
-            id: 'processdatasetid',
-            dataset: {
-              id: 'datasetid'
-            },
-            sets: ['test']
-          }
-        })
-        assert.deepStrictEqual(state, {
-          processDatasets: {
-            processid: [
+                id: 'processsetid',
+                dataset: { id: 'datasetid' },
+                set_name: 'test'
+              },
               {
-                id: 'processdatasetid',
-                dataset: {
-                  id: 'datasetid'
-                },
-                sets: ['test']
+                id: 'processsetid2',
+                dataset: { id: 'datasetid' },
+                set_name: 'validation'
               }
             ]
           }
@@ -222,45 +241,65 @@ describe('process', () => {
       })
     })
 
-    describe('removeProcessDataset', () => {
-      it('removes a dataset from a process', () => {
+    describe('removeProcessSet', () => {
+      it('removes a set from a process', () => {
         const state = {
-          processDatasets: {
+          processSets: {
             process1: [
-              { id: 'processdataset1' },
-              { id: 'processdataset2' }
+              {
+                id: 'processsetid',
+                dataset: { id: 'datasetid' },
+                set_name: 'test'
+              },
+              {
+                id: 'processsetid',
+                dataset: { id: 'datasetid' },
+                set_name: 'validation'
+              }
             ]
           }
         }
-        mutations.removeProcessDataset(state, {
+        mutations.removeProcessSet(state, {
           processId: 'process1',
-          processDatasetId: 'processdataset1'
+          processSetId: 'processsetid'
         })
         assert.deepStrictEqual(state, {
-          processDatasets: {
+          processSets: {
             process1: [
-              { id: 'processdataset2' }
+              {
+                id: 'processsetid',
+                dataset: { id: 'datasetid' },
+                set_name: 'validation'
+              }
             ]
           }
         })
       })
 
-      it('ignores a dataset that is already removed', () => {
+      it('ignores a set that is already removed', () => {
         const state = {
-          processDatasets: {
+          processSets: {
             process1: [
-              { id: 'processdataset2' }
+              {
+                id: 'processsetid',
+                dataset: { id: 'datasetid' },
+                set_name: 'validation'
+              }
             ]
           }
         }
-        mutations.removeProcessDataset(state, {
+        mutations.removeProcessSet(state, {
           processId: 'process1',
-          processDatasetId: 'processdataset1'
+          processSetId: 'processsetid2'
         })
         assert.deepStrictEqual(state, {
-          processDatasets: {
+          processSets: {
             process1: [
-              { id: 'processdataset2' }
+              {
+                id: 'processsetid',
+                dataset: { id: 'datasetid' },
+                set_name: 'validation'
+              }
             ]
           }
         })
@@ -648,9 +687,13 @@ describe('process', () => {
         processWorkerRuns: workerRunsSample,
         processes: processesSample,
         processElementsPage: processElementsSample,
-        processDatasets: {
+        processSets: {
           processid: [
-            { id: 'datasetid' }
+            {
+              id: 'processsetid',
+              dataset: { id: 'datasetid' },
+              set_name: 'test'
+            }
           ]
         },
         tasks: { [taskSample.id]: taskSample },
@@ -660,7 +703,7 @@ describe('process', () => {
       mutations.reset(state)
       assert.deepStrictEqual(state, {
         processWorkerRuns: {},
-        processDatasets: {},
+        processSets: {},
         processes: {},
         processPage: {},
         processElementsPage: {},
@@ -1162,111 +1205,114 @@ describe('process', () => {
       })
     })
 
-    describe('listProcessDatasets', () => {
-      it('lists datasets on a process', async () => {
-        const dataset1 = {
-          id: 'dataset1',
-          name: 'The Chosen One'
+    describe('listProcessSets', () => {
+      it('lists sets on a process', async () => {
+        const set1 = {
+          id: 'processsetid',
+          dataset: { id: 'datasetid' },
+          set_name: 'test'
         }
-        const dataset2 = {
-          id: 'dataset2',
-          name: 'Plan B'
+        const set2 = {
+          id: 'processsetid2',
+          dataset: { id: 'datasetid' },
+          set_name: 'validation'
         }
-        mock.onGet('/process/processid/datasets/', { params: { page: 1 } }).reply(200, {
+        mock.onGet('/process/processid/sets/', { params: { page: 1 } }).reply(200, {
           count: 2,
           previous: null,
-          next: '/process/processid/datasets/?page=2',
+          next: '/process/processid/sets/?page=2',
           number: 1,
-          results: [dataset1]
+          results: [set1]
         })
-        mock.onGet('/process/processid/datasets/', { params: { page: 2 } }).reply(200, {
+        mock.onGet('/process/processid/sets/', { params: { page: 2 } }).reply(200, {
           count: 2,
-          previous: '/process/processid/datasets/?page=1',
+          previous: '/process/processid/sets/?page=1',
           next: null,
           number: 2,
-          results: [dataset2]
+          results: [set2]
         })
 
-        await store.dispatch('process/listProcessDatasets', { processId: 'processid' })
+        await store.dispatch('process/listProcessSets', { processId: 'processid' })
 
         assert.deepStrictEqual(store.history, [
           {
-            action: 'process/listProcessDatasets',
+            action: 'process/listProcessSets',
             payload: {
               processId: 'processid'
             }
           },
           {
-            mutation: 'process/setProcessDatasets',
+            mutation: 'process/setProcessSets',
             payload: {
               processId: 'processid',
               results: [
-                dataset1
+                set1
               ]
             }
           },
           {
-            action: 'process/listProcessDatasets',
+            action: 'process/listProcessSets',
             payload: {
               processId: 'processid',
               page: 2
             }
           },
           {
-            mutation: 'process/setProcessDatasets',
+            mutation: 'process/setProcessSets',
             payload: {
               processId: 'processid',
               results: [
-                dataset2
+                set2
               ]
             }
           }
         ])
 
-        assert.deepStrictEqual(store.state.process.processDatasets, {
+        assert.deepStrictEqual(store.state.process.processSets, {
           processid: [
-            dataset1,
-            dataset2
+            set1,
+            set2
           ]
         })
       })
 
       it('detects pagination errors', async () => {
-        const dataset1 = {
-          id: 'dataset1',
-          name: 'The Chosen One'
+        const set1 = {
+          id: 'processsetid',
+          dataset: { id: 'datasetid' },
+          set_name: 'test'
         }
         // All requests on any page number will return page 1
-        mock.onGet('/process/processid/datasets/').reply(200, {
+        mock.onGet('/process/processid/sets/').reply(200, {
           count: 2,
           previous: null,
-          next: '/process/processid/datasets/?page=2',
+          next: '/process/processid/sets/?page=2',
           number: 1,
-          results: [dataset1]
+          results: [set1]
         })
 
-        const err = await assertRejects(async () => store.dispatch('process/listProcessDatasets', { processId: 'processid' }))
+        const err = await assertRejects(async () => store.dispatch('process/listProcessSets', { processId: 'processid' }))
 
-        assert.strictEqual(err.toString(), 'Error: Pagination failed while listing datasets for process "processid"')
+        assert.strictEqual(err.toString(), 'Error: Pagination failed while listing sets for process "processid"')
 
         assert.deepStrictEqual(store.history, [
           {
-            action: 'process/listProcessDatasets',
+            action: 'process/listProcessSets',
             payload: {
               processId: 'processid'
             }
           },
           {
-            mutation: 'process/setProcessDatasets',
+            mutation: 'process/setProcessSets',
             payload: {
               processId: 'processid',
               results: [
-                dataset1
+                set1
               ]
             }
           },
           {
-            action: 'process/listProcessDatasets',
+            action: 'process/listProcessSets',
             payload: {
               processId: 'processid',
               page: 2
@@ -1274,262 +1320,196 @@ describe('process', () => {
           }
         ])
 
-        assert.deepStrictEqual(store.state.process.processDatasets, {
+        assert.deepStrictEqual(store.state.process.processSets, {
           processid: [
-            dataset1
+            set1
           ]
         })
       })
 
       it('throws other errors', async () => {
-        const dataset1 = {
-          id: 'dataset1',
-          name: 'The Chosen One'
+        const set1 = {
+          id: 'processsetid',
+          dataset: { id: 'datasetid' },
+          set_name: 'test'
         }
-        mock.onGet('/process/processid/datasets/', { params: { page: 1 } }).reply(200, {
+        mock.onGet('/process/processid/sets/', { params: { page: 1 } }).reply(200, {
           count: 2,
           previous: null,
-          next: '/process/processid/datasets/?page=2',
+          next: '/process/processid/sets/?page=2',
           number: 1,
-          results: [dataset1]
+          results: [set1]
         })
-        mock.onGet('/process/processid/datasets/', { params: { page: 2 } }).reply(418)
+        mock.onGet('/process/processid/sets/', { params: { page: 2 } }).reply(418)
 
-        const err = await assertRejects(async () => store.dispatch('process/listProcessDatasets', { processId: 'processid' }))
+        const err = await assertRejects(async () => store.dispatch('process/listProcessSets', { processId: 'processid' }))
 
         assert.strictEqual(err.toString(), 'Error: Request failed with status code 418')
 
         assert.deepStrictEqual(store.history, [
           {
-            action: 'process/listProcessDatasets',
+            action: 'process/listProcessSets',
             payload: {
               processId: 'processid'
             }
           },
           {
-            mutation: 'process/setProcessDatasets',
+            mutation: 'process/setProcessSets',
             payload: {
               processId: 'processid',
               results: [
-                dataset1
+                set1
               ]
             }
           },
           {
-            action: 'process/listProcessDatasets',
+            action: 'process/listProcessSets',
             payload: {
               processId: 'processid',
               page: 2
             }
+          },
+          {
+            mutation: 'notifications/notify',
+            payload: {
+              text: 'Request failed with status code 418',
+              type: 'error'
+            }
           }
         ])
 
-        assert.deepStrictEqual(store.state.process.processDatasets, {
+        assert.deepStrictEqual(store.state.process.processSets, {
           processid: [
-            dataset1
+            set1
           ]
         })
       })
 
-      it('does not list datasets if they were already listed', async () => {
+      it('does not list sets if they were already listed', async () => {
         // The array exists for this process, meaning a list already ran
-        store.state.process.processDatasets.processid = []
+        store.state.process.processSets.processid = []
 
-        await store.dispatch('process/listProcessDatasets', { processId: 'processid' })
+        await store.dispatch('process/listProcessSets', { processId: 'processid' })
 
         assert.deepStrictEqual(store.history, [
           {
-            action: 'process/listProcessDatasets',
+            action: 'process/listProcessSets',
             payload: {
               processId: 'processid'
             }
           }
         ])
 
-        assert.deepStrictEqual(store.state.process.processDatasets, {
+        assert.deepStrictEqual(store.state.process.processSets, {
           processid: []
         })
       })
     })
 
-    describe('createProcessDataset', () => {
-      it('adds a dataset to a process', async () => {
-        mock.onPost('/process/processid/dataset/datasetid/').reply(201, {
-          id: 'processdatasetid',
-          dataset: {
-            id: 'datasetid'
-          },
-          sets: ['test', 'validation']
+    describe('createProcessSet', () => {
+      it('adds a dataset set to a process', async () => {
+        mock.onPost('/process/processid/set/setid/').reply(201, {
+          id: 'processsetid',
+          dataset: { id: 'datasetid' },
+          set_name: 'test'
         })
 
-        await store.dispatch('process/createProcessDataset', {
+        await store.dispatch('process/createProcessSet', {
           processId: 'processid',
-          dataset: { id: 'datasetid' }
+          setId: 'setid'
         })
 
         assert.deepStrictEqual(store.history, [
           {
-            action: 'process/createProcessDataset',
+            action: 'process/createProcessSet',
             payload: {
               processId: 'processid',
-              dataset: { id: 'datasetid' }
+              setId: 'setid'
             }
           },
           {
-            mutation: 'process/setProcessDatasets',
+            mutation: 'process/setProcessSets',
             payload: {
               processId: 'processid',
               results: [
                 {
-                  id: 'processdatasetid',
-                  dataset: {
-                    id: 'datasetid'
-                  },
-                  sets: ['test', 'validation']
+                  id: 'processsetid',
+                  dataset: { id: 'datasetid' },
+                  set_name: 'test'
                 }
               ]
             }
           }
         ])
 
-        assert.deepStrictEqual(store.state.process.processDatasets, {
+        assert.deepStrictEqual(store.state.process.processSets, {
           processid: [
             {
-              id: 'processdatasetid',
-              dataset: {
-                id: 'datasetid'
-              },
-              sets: ['test', 'validation']
+              id: 'processsetid',
+              dataset: { id: 'datasetid' },
+              set_name: 'test'
             }
           ]
         })
       })
     })
 
-    describe('updateProcessDataset', () => {
-      it('updates a process dataset', async () => {
-        store.state.process.processDatasets = {
-          processid: [
-            {
-              id: 'processdatasetid',
-              dataset: {
-                id: 'datasetid'
-              },
-              sets: ['train', 'validation', 'test']
-            }
-          ]
-        }
-
-        mock.onPatch('/process/processid/dataset/datasetid/').reply(201, {
-          id: 'processdatasetid',
-          dataset: {
-            id: 'datasetid'
-          },
-          sets: ['test']
-        })
-
-        await store.dispatch('process/updateProcessDataset', {
-          processId: 'processid',
-          datasetId: 'datasetid',
-          sets: ['test']
-        })
-
-        assert.deepStrictEqual(store.history, [
+    describe('deleteProcessSet', () => {
+      it('removes a set from a process', async () => {
+        store.state.process.processSets.processid = [
           {
-            action: 'process/updateProcessDataset',
-            payload: {
-              processId: 'processid',
-              datasetId: 'datasetid',
-              sets: ['test']
-            }
-          },
-          {
-            mutation: 'process/updateSingleProcessDataset',
-            payload: {
-              processId: 'processid',
-              data: {
-                id: 'processdatasetid',
-                dataset: {
-                  id: 'datasetid'
-                },
-                sets: ['test']
-              }
-            }
-          }
-        ])
-
-        assert.deepStrictEqual(store.state.process.processDatasets, {
-          processid: [
-            {
-              id: 'processdatasetid',
-              dataset: {
-                id: 'datasetid'
-              },
-              sets: ['test']
-            }
-          ]
-        })
-      })
-    })
-
-    describe('deleteProcessDataset', () => {
-      it('removes a dataset from a process', async () => {
-        store.state.process.processDatasets.processid = [
-          {
-            id: 'processdatasetid',
-            dataset: {
-              id: 'datasetid'
-            },
-            sets: ['test', 'validation']
+            id: 'processsetid',
+            dataset: { id: 'datasetid' },
+            set_name: 'test'
           }
         ]
-        mock.onDelete('/process/processid/dataset/datasetid/').reply(201)
+        mock.onDelete('/process/processid/set/setid/').reply(201)
 
-        await store.dispatch('process/deleteProcessDataset', {
+        await store.dispatch('process/deleteProcessSet', {
           processId: 'processid',
-          datasetId: 'datasetid',
-          processDatasetId: 'processdatasetid'
+          setId: 'setid',
+          processSetId: 'processsetid'
         })
 
         assert.deepStrictEqual(store.history, [
           {
-            action: 'process/deleteProcessDataset',
+            action: 'process/deleteProcessSet',
             payload: {
               processId: 'processid',
-              datasetId: 'datasetid',
-              processDatasetId: 'processdatasetid'
+              setId: 'setid',
+              processSetId: 'processsetid'
             }
           },
           {
-            mutation: 'process/removeProcessDataset',
+            mutation: 'process/removeProcessSet',
             payload: {
               processId: 'processid',
-              processDatasetId: 'processdatasetid'
+              processSetId: 'processsetid'
             }
           }
         ])
 
-        assert.deepStrictEqual(store.state.process.processDatasets, {
+        assert.deepStrictEqual(store.state.process.processSets, {
           processid: []
         })
       })
 
       it('notifies of errors', async () => {
-        mock.onDelete('/process/processid/dataset/datasetid/').reply(418)
+        mock.onDelete('/process/processid/set/setid/').reply(418)
 
-        const err = await assertRejects(async () => store.dispatch('process/deleteProcessDataset', {
+        const err = await assertRejects(async () => store.dispatch('process/deleteProcessSet', {
           processId: 'processid',
-          datasetId: 'datasetid'
+          setId: 'setid'
         }))
 
         assert.strictEqual(err.toString(), 'Error: Request failed with status code 418')
 
         assert.deepStrictEqual(store.history, [
           {
-            action: 'process/deleteProcessDataset',
+            action: 'process/deleteProcessSet',
             payload: {
               processId: 'processid',
-              datasetId: 'datasetid'
+              setId: 'setid'
             }
           },
           {
diff --git a/tests/unit/stores/datasets.spec.js b/tests/unit/stores/datasets.spec.js
index b379601c767339755639714a198416e7599d28fa..974816302fc3e6147d838e5563290d48dedf13cc 100644
--- a/tests/unit/stores/datasets.spec.js
+++ b/tests/unit/stores/datasets.spec.js
@@ -41,14 +41,40 @@ describe('datasets', () => {
                 state: 'open',
                 name: 'Shin Seiki Evangelion',
                 description: 'Become legend, young boy!',
-                sets: ['unit-00', 'unit-01', 'unit-02']
+                sets: [
+                  {
+                    name: 'unit-00',
+                    id: 'setid1'
+                  },
+                  {
+                    name: 'unit-01',
+                    id: 'setid2'
+                  },
+                  {
+                    name: 'unit-02',
+                    id: 'setid3'
+                  }
+                ]
               },
               {
                 id: 'bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb',
                 state: 'open',
                 name: 'Mobile Suit Gundam Wing',
                 description: 'another dataset',
-                sets: ['training', 'test', 'validation']
+                sets: [
+                  {
+                    name: 'training',
+                    id: 'setid4'
+                  },
+                  {
+                    name: 'test',
+                    id: 'setid5'
+                  },
+                  {
+                    name: 'validation',
+                    id: 'setid6'
+                  }
+                ]
               }
             ]
           },
@@ -62,7 +88,24 @@ describe('datasets', () => {
                 state: 'open',
                 name: 'Sailor Moon',
                 description: 'Moon prism power, make up!',
-                sets: ['neptune', 'uranus', 'pluto', 'saturn']
+                sets: [
+                  {
+                    name: 'neptune',
+                    id: 'setid7'
+                  },
+                  {
+                    name: 'uranus',
+                    id: 'setid8'
+                  },
+                  {
+                    name: 'pluto',
+                    id: 'setid9'
+                  },
+                  {
+                    name: 'saturn',
+                    id: 'setid10'
+                  }
+                ]
               }
             ]
           }
@@ -86,21 +129,64 @@ describe('datasets', () => {
             state: 'open',
             name: 'Shin Seiki Evangelion',
             description: 'Become legend, young boy!',
-            sets: ['unit-00', 'unit-01', 'unit-02']
+            sets: [
+              {
+                name: 'unit-00',
+                id: 'setid1'
+              },
+              {
+                name: 'unit-01',
+                id: 'setid2'
+              },
+              {
+                name: 'unit-02',
+                id: 'setid3'
+              }
+            ]
           },
           'bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb': {
             id: 'bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb',
             state: 'open',
             name: 'Mobile Suit Gundam Wing',
             description: 'another dataset',
-            sets: ['training', 'test', 'validation']
+            sets: [
+              {
+                name: 'training',
+                id: 'setid4'
+              },
+              {
+                name: 'test',
+                id: 'setid5'
+              },
+              {
+                name: 'validation',
+                id: 'setid6'
+              }
+            ]
           },
           'cccccccc-cccc-cccc-cccc-cccccccccccc': {
             id: 'cccccccc-cccc-cccc-cccc-cccccccccccc',
             state: 'open',
             name: 'Sailor Moon',
             description: 'Moon prism power, make up!',
-            sets: ['neptune', 'uranus', 'pluto', 'saturn']
+            sets: [
+              {
+                name: 'neptune',
+                id: 'setid7'
+              },
+              {
+                name: 'uranus',
+                id: 'setid8'
+              },
+              {
+                name: 'pluto',
+                id: 'setid9'
+              },
+              {
+                name: 'saturn',
+                id: 'setid10'
+              }
+            ]
           }
         })
       })
@@ -120,20 +206,63 @@ describe('datasets', () => {
             state: 'open',
             name: 'Shin Seiki Evangelion',
             description: 'Become legend, young boy!',
-            sets: ['unit-00', 'unit-01', 'unit-02']
+            sets: [
+              {
+                name: 'unit-00',
+                id: 'setid1'
+              },
+              {
+                name: 'unit-01',
+                id: 'setid2'
+              },
+              {
+                name: 'unit-02',
+                id: 'setid3'
+              }
+            ]
           },
           'bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb': {
             id: 'bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb',
             state: 'open',
             name: 'Mobile Suit Gundam Wing',
             description: 'another dataset',
-            sets: ['training', 'test', 'validation']
+            sets: [
+              {
+                name: 'training',
+                id: 'setid4'
+              },
+              {
+                name: 'test',
+                id: 'setid5'
+              },
+              {
+                name: 'validation',
+                id: 'setid6'
+              }
+            ]
           }
         }
         const requestData = {
           name: 'Sailor Moon',
           description: 'Moon prism power, make up!',
-          sets: ['neptune', 'uranus', 'pluto', 'saturn']
+          sets: [
+            {
+              name: 'neptune',
+              id: 'setid7'
+            },
+            {
+              name: 'uranus',
+              id: 'setid8'
+            },
+            {
+              name: 'pluto',
+              id: 'setid9'
+            },
+            {
+              name: 'saturn',
+              id: 'setid10'
+            }
+          ]
         }
         const responseData = {
           ...requestData,
@@ -150,21 +279,64 @@ describe('datasets', () => {
             state: 'open',
             name: 'Shin Seiki Evangelion',
             description: 'Become legend, young boy!',
-            sets: ['unit-00', 'unit-01', 'unit-02']
+            sets: [
+              {
+                name: 'unit-00',
+                id: 'setid1'
+              },
+              {
+                name: 'unit-01',
+                id: 'setid2'
+              },
+              {
+                name: 'unit-02',
+                id: 'setid3'
+              }
+            ]
           },
           'bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb': {
             id: 'bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb',
             state: 'open',
             name: 'Mobile Suit Gundam Wing',
             description: 'another dataset',
-            sets: ['training', 'test', 'validation']
+            sets: [
+              {
+                name: 'training',
+                id: 'setid4'
+              },
+              {
+                name: 'test',
+                id: 'setid5'
+              },
+              {
+                name: 'validation',
+                id: 'setid6'
+              }
+            ]
           },
           'cccccccc-cccc-cccc-cccc-cccccccccccc': {
             id: 'cccccccc-cccc-cccc-cccc-cccccccccccc',
             state: 'open',
             name: 'Sailor Moon',
             description: 'Moon prism power, make up!',
-            sets: ['neptune', 'uranus', 'pluto', 'saturn']
+            sets: [
+              {
+                name: 'neptune',
+                id: 'setid7'
+              },
+              {
+                name: 'uranus',
+                id: 'setid8'
+              },
+              {
+                name: 'pluto',
+                id: 'setid9'
+              },
+              {
+                name: 'saturn',
+                id: 'setid10'
+              }
+            ]
           }
         })
         assert.deepStrictEqual(store.corpusDatasets, {
@@ -202,14 +374,40 @@ describe('datasets', () => {
             state: 'open',
             name: 'Shin Seiki Evangelion',
             description: 'Become legend, young boy!',
-            sets: ['unit-00', 'unit-01', 'unit-02']
+            sets: [
+              {
+                name: 'unit-00',
+                id: 'setid1'
+              },
+              {
+                name: 'unit-01',
+                id: 'setid2'
+              },
+              {
+                name: 'unit-02',
+                id: 'setid3'
+              }
+            ]
           },
           'bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb': {
             id: 'bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb',
             state: 'open',
             name: 'Mobile Suit Gundam Wing',
             description: 'another dataset',
-            sets: ['training', 'test', 'validation']
+            sets: [
+              {
+                name: 'training',
+                id: 'setid4'
+              },
+              {
+                name: 'test',
+                id: 'setid5'
+              },
+              {
+                name: 'validation',
+                id: 'setid6'
+              }
+            ]
           }
         }
         store.corpusDatasets = {
@@ -223,7 +421,20 @@ describe('datasets', () => {
           state: 'open',
           name: 'Pretty Guardian Sailor Moon',
           description: 'another dataset',
-          sets: ['training', 'test', 'validation']
+          sets: [
+            {
+              name: 'training',
+              id: 'setid4'
+            },
+            {
+              name: 'test',
+              id: 'setid5'
+            },
+            {
+              name: 'validation',
+              id: 'setid6'
+            }
+          ]
         }
         mock.onPatch('/datasets/bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb/', { name: 'Pretty Guardian Sailor Moon' }).reply(200, { ...responseData })
 
@@ -240,14 +451,40 @@ describe('datasets', () => {
             state: 'open',
             name: 'Shin Seiki Evangelion',
             description: 'Become legend, young boy!',
-            sets: ['unit-00', 'unit-01', 'unit-02']
+            sets: [
+              {
+                name: 'unit-00',
+                id: 'setid1'
+              },
+              {
+                name: 'unit-01',
+                id: 'setid2'
+              },
+              {
+                name: 'unit-02',
+                id: 'setid3'
+              }
+            ]
           },
           'bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb': {
             id: 'bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb',
             state: 'open',
             name: 'Pretty Guardian Sailor Moon',
             description: 'another dataset',
-            sets: ['training', 'test', 'validation']
+            sets: [
+              {
+                name: 'training',
+                id: 'setid4'
+              },
+              {
+                name: 'test',
+                id: 'setid5'
+              },
+              {
+                name: 'validation',
+                id: 'setid6'
+              }
+            ]
           }
         })
         assert.deepStrictEqual(store.corpusDatasets, {
@@ -284,14 +521,40 @@ describe('datasets', () => {
             state: 'open',
             name: 'Shin Seiki Evangelion',
             description: 'Become legend, young boy!',
-            sets: ['unit-00', 'unit-01', 'unit-02']
+            sets: [
+              {
+                name: 'unit-00',
+                id: 'setid1'
+              },
+              {
+                name: 'unit-01',
+                id: 'setid2'
+              },
+              {
+                name: 'unit-02',
+                id: 'setid3'
+              }
+            ]
           },
           'bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb': {
             id: 'bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb',
             state: 'open',
             name: 'Mobile Suit Gundam Wing',
             description: 'another dataset',
-            sets: ['training', 'test', 'validation']
+            sets: [
+              {
+                name: 'training',
+                id: 'setid4'
+              },
+              {
+                name: 'test',
+                id: 'setid5'
+              },
+              {
+                name: 'validation',
+                id: 'setid6'
+              }
+            ]
           }
         }
         store.corpusDatasets = {
@@ -313,7 +576,20 @@ describe('datasets', () => {
             state: 'open',
             name: 'Shin Seiki Evangelion',
             description: 'Become legend, young boy!',
-            sets: ['unit-00', 'unit-01', 'unit-02']
+            sets: [
+              {
+                name: 'unit-00',
+                id: 'setid1'
+              },
+              {
+                name: 'unit-01',
+                id: 'setid2'
+              },
+              {
+                name: 'unit-02',
+                id: 'setid3'
+              }
+            ]
           }
         })
         assert.deepStrictEqual(store.corpusDatasets, {
@@ -341,16 +617,15 @@ describe('datasets', () => {
 
     describe('addDatasetElementsSelection', () => {
       it('adds elements from a selection to a dataset', async () => {
-        mock.onPost('/corpus/corpusid/datasets/selection/', { dataset_id: 'datasetid', set: 'test' }).reply(204)
+        mock.onPost('/corpus/corpusid/datasets/selection/', { set_id: 'setid' }).reply(204)
 
-        const params = { dataset_id: 'datasetid', set: 'test' }
-        await store.addDatasetElementsSelection('corpusid', params)
+        await store.addDatasetElementsSelection('corpusid', 'datasetid', { id: 'setid', name: 'setname' })
 
         assert.deepStrictEqual(notificationStore.notifications, [
           {
             id: 0,
             type: 'success',
-            text: 'Elements have been added to the dataset.'
+            text: 'Elements have been added to the dataset set.'
           }
         ])
       })
@@ -369,9 +644,8 @@ describe('datasets', () => {
           }
         }
 
-        const params = { dataset_id: 'datasetid', set: 'test' }
-        mock.onPost('/corpus/corpusid/datasets/selection/', params).reply(204)
-        await store.addDatasetElementsSelection('corpusid', params)
+        mock.onPost('/corpus/corpusid/datasets/selection/', { set_id: 'setid' }).reply(204)
+        await store.addDatasetElementsSelection('corpusid', 'datasetid', { id: 'setid', name: 'test' })
 
         assert.deepStrictEqual(store.datasetElementPagination, {
           datasetid: {
@@ -384,11 +658,10 @@ describe('datasets', () => {
       })
 
       it('throw errors', async () => {
-        mock.onPost('/corpus/corpusid/datasets/selection/', { dataset_id: 'datasetid', set: 'test' }).reply(400)
+        mock.onPost('/corpus/corpusid/datasets/selection/', { set_id: 'setid' }).reply(400)
 
-        const params = { dataset_id: 'datasetid', set: 'test' }
         await assertRejects(
-          async () => await store.addDatasetElementsSelection('corpusid', params)
+          async () => await store.addDatasetElementsSelection('corpusid', 'datasetid', { id: 'setid', name: 'setname' })
         )
 
         assert.deepStrictEqual(notificationStore.notifications, [
@@ -409,21 +682,64 @@ describe('datasets', () => {
             state: 'open',
             name: 'Shin Seiki Evangelion',
             description: 'Become legend, young boy!',
-            sets: ['unit-00', 'unit-01', 'unit-02']
+            sets: [
+              {
+                name: 'unit-00',
+                id: 'setid1'
+              },
+              {
+                name: 'unit-01',
+                id: 'setid2'
+              },
+              {
+                name: 'unit-02',
+                id: 'setid3'
+              }
+            ]
           },
           'bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb': {
             id: 'bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb',
             state: 'open',
             name: 'Mobile Suit Gundam Wing',
             description: 'another dataset',
-            sets: ['training', 'test', 'validation']
+            sets: [
+              {
+                name: 'training',
+                id: 'setid4'
+              },
+              {
+                name: 'test',
+                id: 'setid5'
+              },
+              {
+                name: 'validation',
+                id: 'setid6'
+              }
+            ]
           },
           'cccccccc-cccc-cccc-cccc-cccccccccccc': {
             id: 'cccccccc-cccc-cccc-cccc-cccccccccccc',
             state: 'open',
             name: 'Sailor Moon',
             description: 'Moon prism power, make up!',
-            sets: ['neptune', 'uranus', 'pluto', 'saturn']
+            sets: [
+              {
+                name: 'neptune',
+                id: 'setid7'
+              },
+              {
+                name: 'uranus',
+                id: 'setid8'
+              },
+              {
+                name: 'pluto',
+                id: 'setid9'
+              },
+              {
+                name: 'saturn',
+                id: 'setid10'
+              }
+            ]
           }
         }
         store.corpusDatasets = {
@@ -440,14 +756,44 @@ describe('datasets', () => {
             state: 'open',
             name: 'Mobile Suit Gundam Wing',
             description: 'another dataset',
-            sets: ['training', 'test', 'validation']
+            sets: [
+              {
+                name: 'training',
+                id: 'setid4'
+              },
+              {
+                name: 'test',
+                id: 'setid5'
+              },
+              {
+                name: 'validation',
+                id: 'setid6'
+              }
+            ]
           },
           {
             id: 'cccccccc-cccc-cccc-cccc-cccccccccccc',
             state: 'open',
             name: 'Sailor Moon',
             description: 'Moon prism power, make up!',
-            sets: ['neptune', 'uranus', 'pluto', 'saturn']
+            sets: [
+              {
+                name: 'neptune',
+                id: 'setid7'
+              },
+              {
+                name: 'uranus',
+                id: 'setid8'
+              },
+              {
+                name: 'pluto',
+                id: 'setid9'
+              },
+              {
+                name: 'saturn',
+                id: 'setid10'
+              }
+            ]
           }
         ])
       })
@@ -459,7 +805,20 @@ describe('datasets', () => {
             state: 'open',
             name: 'Shin Seiki Evangelion',
             description: 'Become legend, young boy!',
-            sets: ['unit-00', 'unit-01', 'unit-02']
+            sets: [
+              {
+                name: 'unit-00',
+                id: 'setid1'
+              },
+              {
+                name: 'unit-01',
+                id: 'setid2'
+              },
+              {
+                name: 'unit-02',
+                id: 'setid3'
+              }
+            ]
           }
         }
         store.corpusDatasets = {
@@ -668,7 +1027,7 @@ describe('datasets', () => {
             }
           }
         }
-        elementsStore.elementDatasets = {
+        elementsStore.elementDatasetSets = {
           2: [
             {
               set: 'unit-00',
@@ -677,7 +1036,20 @@ describe('datasets', () => {
                 state: 'open',
                 name: 'Shin Seiki Evangelion',
                 description: 'Become legend, young boy!',
-                sets: ['unit-00', 'unit-01', 'unit-02']
+                sets: [
+                  {
+                    name: 'unit-00',
+                    id: 'setid1'
+                  },
+                  {
+                    name: 'unit-01',
+                    id: 'setid2'
+                  },
+                  {
+                    name: 'unit-02',
+                    id: 'setid3'
+                  }
+                ]
               }
             },
             {
@@ -687,7 +1059,20 @@ describe('datasets', () => {
                 state: 'open',
                 name: 'Set 01',
                 description: 'A',
-                sets: ['unit-00', 'unit-01', 'unit-02']
+                sets: [
+                  {
+                    name: 'unit-00',
+                    id: 'setid1'
+                  },
+                  {
+                    name: 'unit-01',
+                    id: 'setid2'
+                  },
+                  {
+                    name: 'unit-02',
+                    id: 'setid3'
+                  }
+                ]
               }
             }
           ]
@@ -706,7 +1091,7 @@ describe('datasets', () => {
             }
           }
         })
-        assert.deepStrictEqual(elementsStore.elementDatasets, {
+        assert.deepStrictEqual(elementsStore.elementDatasetSets, {
           2: [
             {
               set: 'unit-01',
@@ -715,7 +1100,20 @@ describe('datasets', () => {
                 state: 'open',
                 name: 'Set 01',
                 description: 'A',
-                sets: ['unit-00', 'unit-01', 'unit-02']
+                sets: [
+                  {
+                    name: 'unit-00',
+                    id: 'setid1'
+                  },
+                  {
+                    name: 'unit-01',
+                    id: 'setid2'
+                  },
+                  {
+                    name: 'unit-02',
+                    id: 'setid3'
+                  }
+                ]
               }
             }
           ]
diff --git a/tests/unit/stores/elements.spec.js b/tests/unit/stores/elements.spec.js
index 4e9e8f3497353c9dd4a3ded614185f9d08b308b7..0f40024d5ec6d14f211cc13c838e2a5e8fffd9c2 100644
--- a/tests/unit/stores/elements.spec.js
+++ b/tests/unit/stores/elements.spec.js
@@ -27,10 +27,10 @@ describe('elements.ts', () => {
   })
 
   describe('actions', () => {
-    describe('listElementDatasets', () => {
+    describe('listElementDatasetSets', () => {
       it('returns early in case datasets have been fetched', async () => {
-        store.elementDatasets = { element_id: [] }
-        await store.listElementDatasets('element_id')
+        store.elementDatasetSets = { element_id: [] }
+        await store.listElementDatasetSets('element_id')
         await store.actionsCompleted()
       })
 
@@ -38,7 +38,7 @@ describe('elements.ts', () => {
         const pagination1 = {
           count: 2,
           previous: null,
-          next: '/element/element_id/datasets/?page=2',
+          next: '/element/element_id/sets/?page=2',
           number: 1,
           results: [elementDatasetsSample.results[0]]
         }
@@ -49,13 +49,13 @@ describe('elements.ts', () => {
           number: 2,
           results: [elementDatasetsSample.results[1]]
         }
-        mock.onGet('/element/element_id/datasets/', { params: {} }).reply(200, pagination1)
-        mock.onGet('/element/element_id/datasets/', { params: { page: 2 } }).reply(200, pagination2)
+        mock.onGet('/element/element_id/sets/', { params: {} }).reply(200, pagination1)
+        mock.onGet('/element/element_id/sets/', { params: { page: 2 } }).reply(200, pagination2)
 
-        await store.listElementDatasets('element_id')
+        await store.listElementDatasetSets('element_id')
         await store.actionsCompleted()
 
-        assert.deepStrictEqual(store.elementDatasets, {
+        assert.deepStrictEqual(store.elementDatasetSets, {
           element_id: [
             elementDatasetsSample.results[0],
             elementDatasetsSample.results[1]
@@ -64,10 +64,10 @@ describe('elements.ts', () => {
       })
 
       it('handles errors', async () => {
-        mock.onGet('/element/element_id/datasets/').reply(403, { detail: ['Forbidden'] })
+        mock.onGet('/element/element_id/sets/').reply(403, { detail: ['Forbidden'] })
 
         await assertRejects(async () =>
-          await store.listElementDatasets('element_id')
+          await store.listElementDatasetSets('element_id')
         )
         assert.deepStrictEqual(notificationStore.notifications, [
           { id: 0, type: 'error', text: 'Forbidden' }