diff --git a/CHANGELOG b/CHANGELOG
index 770eb87d67eb48e86ce72232d866fb8c5d02aad9..939d12aeb9efb56e39f375c096b93e9bee260fb5 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,6 +1,6 @@
 smasch (1.3.1-1) stable; urgency=medium
-  
-  * dependencies: updated django related python packages to fix 
+
+  * dependencies: updated django related python packages to fix
     deprectation warnings
   * dependencies: updated python related dependencies
   * dependencies: updated npm packages and removed unused packages
@@ -10,6 +10,11 @@ smasch (1.3.1-1) stable; urgency=medium
 
   -- Carlos Vega <carlos.vega@lih.lu> Tue, 7 Nov 2023 10:35:00 +0200
 
+smasch (1.3.0-2) stable; urgency=low
+  * small improvement: add provenance tracking option "tracked" to CustomStudySubjectField (#523)
+
+  -- Nirmeen Sallam <nirmeen.sallam@uni.lu> Fri, 04 Sep 2023 16:00:00 +0200
+
 smasch (1.3.0-1) stable; urgency=medium
 
   * small improvement: enable adding phone numbers to subject columns (#519)
diff --git a/smash/web/migrations/0212_customstudysubjectfield_tracked.py b/smash/web/migrations/0212_customstudysubjectfield_tracked.py
new file mode 100644
index 0000000000000000000000000000000000000000..b8c60e2b7a6a898c3256b040f8c068178a7c331e
--- /dev/null
+++ b/smash/web/migrations/0212_customstudysubjectfield_tracked.py
@@ -0,0 +1,18 @@
+# Generated by Django 3.1.4 on 2023-07-07 14:37
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('web', '0211_subjectexport_data'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='customstudysubjectfield',
+            name='tracked',
+            field=models.BooleanField(default=False),
+        ),
+    ]
diff --git a/smash/web/models/custom_data/custom_study_subject_field.py b/smash/web/models/custom_data/custom_study_subject_field.py
index 6db608d73491f0504e3d590c841d50d6b8afc636..9d754d61041e4f09987faeb3d627390516414d90 100644
--- a/smash/web/models/custom_data/custom_study_subject_field.py
+++ b/smash/web/models/custom_data/custom_study_subject_field.py
@@ -18,6 +18,8 @@ class CustomStudySubjectField(models.Model):
 
     unique = models.BooleanField(default=False)
 
+    tracked = models.BooleanField(default=False)
+
     study = models.ForeignKey("web.Study",
                               verbose_name='Study',
                               editable=False,
diff --git a/smash/web/templates/includes/custom_study_subject_field_box.html b/smash/web/templates/includes/custom_study_subject_field_box.html
index 23e0c461a4eaaaaa706035498bb2b5f494b8350d..fc3de49ed0697cbebe9d073932cc7e6ffffe4e1a 100644
--- a/smash/web/templates/includes/custom_study_subject_field_box.html
+++ b/smash/web/templates/includes/custom_study_subject_field_box.html
@@ -17,6 +17,7 @@
                         <th class="text-center">Default value</th>
                         <th class="text-center">Readonly</th>
                         <th class="text-center">Unique</th>
+                        <th class="text-center">Tracked</th>
                         <th class="text-center">Obligatory</th>
                         <th class="text-center">Edit</th>
                         <th class="text-center">Remove</th>
@@ -30,6 +31,7 @@
                             <td class="text-center">{{ field.default_value }}</td>
                             <td class="text-center">{% if field.readonly %}YES{% else %}NO{% endif %}</td>
                             <td class="text-center">{% if field.unique %}YES{% else %}NO{% endif %}</td>
+                            <td class="text-center">{% if field.tracked %}YES{% else %}NO{% endif %}</td>
                             <td class="text-center">{% if field.obligatory %}YES{% else %}NO{% endif %}</td>
                             <td class="text-center"><a title="edit field"
                                                        href="{% url 'web.views.custom_study_subject_field_edit' field.study.id field.id %}"
diff --git a/smash/web/tests/forms/test_CustomStudySubjectFieldAddForm.py b/smash/web/tests/forms/test_CustomStudySubjectFieldAddForm.py
index 283f431e60717ae5905f2ec5b39fe840edda805d..a96a7aff4f62f6d1c046be6f825416d44df43907 100644
--- a/smash/web/tests/forms/test_CustomStudySubjectFieldAddForm.py
+++ b/smash/web/tests/forms/test_CustomStudySubjectFieldAddForm.py
@@ -24,13 +24,15 @@ class CustomStudySubjectFieldAddFormTest(TestCase):
         ('select list invalid', CUSTOM_FIELD_TYPE_SELECT_LIST, 'bla', False, 'abc;def'),
         ('file', CUSTOM_FIELD_TYPE_FILE, None, True),
         ('file invalid', CUSTOM_FIELD_TYPE_FILE, 'tmp', False),
+        ('text', CUSTOM_FIELD_TYPE_TEXT, 'bla', True, True),
     ])
-    def test_add_field(self, _, field_type, default_value, valid, possible_values=''):
+    def test_add_field(self, _, field_type, default_value, valid, possible_values='', tracked=False):
         sample_data = {
             'default_value': default_value,
             'name': '1. name',
             'type': field_type,
             'possible_values': possible_values,
+            'tracked': tracked,
         }
 
         form = CustomStudySubjectFieldAddForm(sample_data, study=get_test_study())
@@ -40,5 +42,6 @@ class CustomStudySubjectFieldAddFormTest(TestCase):
 
             self.assertEqual(1, CustomStudySubjectField.objects.filter(id=field.id).count())
             self.assertEqual(default_value, field.default_value)
+            self.assertEqual(tracked, field.tracked)
         else:
             self.assertFalse(form.is_valid())
diff --git a/smash/web/tests/forms/test_CustomStudySubjectFieldEditForm.py b/smash/web/tests/forms/test_CustomStudySubjectFieldEditForm.py
index 7436c8c6cd8e181324174d74282220ec353e62c9..cdbd8aa67710d2f5f70f9f32299e07eeacec1792 100644
--- a/smash/web/tests/forms/test_CustomStudySubjectFieldEditForm.py
+++ b/smash/web/tests/forms/test_CustomStudySubjectFieldEditForm.py
@@ -24,8 +24,9 @@ class CustomStudySubjectFieldEditFormTest(TestCase):
         ('select list invalid', CUSTOM_FIELD_TYPE_SELECT_LIST, 'bla', False, 'abc;def'),
         ('file', CUSTOM_FIELD_TYPE_FILE, None, True),
         ('file invalid', CUSTOM_FIELD_TYPE_FILE, 'tmp', False),
+        ('text', CUSTOM_FIELD_TYPE_TEXT, 'bla', True, True),
     ])
-    def test_edit_field(self, _, field_type, default_value, valid, possible_values=''):
+    def test_edit_field(self, _, field_type, default_value, valid, possible_values='', tracked=False):
         field = CustomStudySubjectField.objects.create(study=get_test_study(), default_value="", type=field_type)
 
         sample_data = {
@@ -33,6 +34,7 @@ class CustomStudySubjectFieldEditFormTest(TestCase):
             'name': '1. name',
             'type': field_type,
             'possible_values': possible_values,
+            'tracked': tracked,
         }
 
         form = CustomStudySubjectFieldEditForm(sample_data, instance=field)
@@ -41,5 +43,6 @@ class CustomStudySubjectFieldEditFormTest(TestCase):
             form.save()
 
             self.assertEqual(default_value, field.default_value)
+            self.assertEqual(tracked, field.tracked)
         else:
             self.assertFalse(form.is_valid())
diff --git a/smash/web/tests/models/test_study_subject.py b/smash/web/tests/models/test_study_subject.py
index 727366a45e442879f38c1d34293d55f58dc7e971..d44f5aba5d684d3d97fc6483ce482b76a314bf26 100644
--- a/smash/web/tests/models/test_study_subject.py
+++ b/smash/web/tests/models/test_study_subject.py
@@ -197,6 +197,33 @@ class SubjectModelTests(TestCase):
 
         self.assertEqual(0, len(subject.custom_data_values))
 
+    def test_subject_with_custom_field_tracked(self):
+        CustomStudySubjectField.objects.create(study=get_test_study(), default_value="xyz", type=CUSTOM_FIELD_TYPE_TEXT,
+                                               tracked=True)
+
+        subject = create_study_subject()
+
+        self.assertEqual(1, len(subject.custom_data_values))
+        value = subject.custom_data_values[0]
+        self.assertEqual("xyz", value.value)
+        self.assertTrue(value.study_subject_field.tracked)
+
+    def test_subject_with_removed_custom_field_not_tracked(self):
+        field = CustomStudySubjectField.objects.create(study=get_test_study(), default_value="xyz",
+                                                       type=CUSTOM_FIELD_TYPE_TEXT)
+
+        subject = create_study_subject()
+        self.assertEqual(1, len(subject.custom_data_values))
+        self.assertFalse(subject.custom_data_values[0].study_subject_field.tracked)
+        field.delete()
+
+        CustomStudySubjectField.objects.create(study=get_test_study(), default_value="xyz", type=CUSTOM_FIELD_TYPE_TEXT,
+                                               tracked=False)
+
+        subject = create_study_subject()
+        self.assertEqual(1, len(subject.custom_data_values))
+        self.assertFalse(subject.custom_data_values[0].study_subject_field.tracked)
+
     def test_subject_with_field_from_different_study(self):
         field = CustomStudySubjectField.objects.create(study=create_study("bla"), type=CUSTOM_FIELD_TYPE_TEXT)
 
diff --git a/smash/web/tests/view/test_subjects.py b/smash/web/tests/view/test_subjects.py
index ffa66975d88f3c19b684aa65b2c0fbfd6b410320..a15d3051c22ef002330c1cd467aba10daf1c5dbe 100644
--- a/smash/web/tests/view/test_subjects.py
+++ b/smash/web/tests/view/test_subjects.py
@@ -8,7 +8,7 @@ from django.urls import reverse
 from web.forms import SubjectAddForm, SubjectEditForm, StudySubjectAddForm, StudySubjectEditForm
 from web.models import MailTemplate, StudySubject, StudyColumns, Visit, Provenance, Subject
 from web.models.constants import SEX_CHOICES_MALE, COUNTRY_AFGHANISTAN_ID, COUNTRY_OTHER_ID, \
-    MAIL_TEMPLATE_CONTEXT_SUBJECT, CUSTOM_FIELD_TYPE_FILE
+    MAIL_TEMPLATE_CONTEXT_SUBJECT, CUSTOM_FIELD_TYPE_FILE, CUSTOM_FIELD_TYPE_TEXT
 from web.models.custom_data import CustomStudySubjectField
 from web.models.custom_data.custom_study_subject_field import get_study_subject_field_id
 from web.tests import LoggedInTestCase
@@ -411,3 +411,94 @@ class SubjectsViewTests(LoggedInTestCase):
         self.client.post(reverse('web.views.subject_add', kwargs={'study_id': self.study.id}), data=form_data)
 
         self.assertEqual(count + 1, StudySubject.objects.all().count())
+
+    def test_subjects_add_subject_with_custom_file_field_tracked(self):
+        field = CustomStudySubjectField.objects.create(name="test-tracked", study=self.study,
+                                                       type=CUSTOM_FIELD_TYPE_TEXT, tracked=True)
+        self.worker.roles.all()[0].permissions.add(Permission.objects.get(codename="add_subject"))
+        self.worker.save()
+        form_data = self.create_add_form_data_for_study_subject()
+
+        form_data["study_subject-" + get_study_subject_field_id(field)] = "BLA"
+        response = self.client.post(reverse('web.views.subject_add', kwargs={'study_id': self.study.id}),
+                                    data=form_data)
+
+        self.assertEqual(response.status_code, 302)
+
+        subject = StudySubject.objects.all().order_by("-id")[0]
+
+        self.assertTrue('BLA' in subject.get_custom_data_value(field).value)
+        self.assertTrue(len(subject.provenances) == 0)
+
+        form_data["study_subject-" + get_study_subject_field_id(field)] = "BLABLA"
+        response = self.client.post(reverse('web.views.subject_edit', kwargs={'subject_id': subject.id}),
+                                    data=form_data)
+
+        self.assertEqual(response.status_code, 302)
+        self.assertTrue('BLABLA' in subject.get_custom_data_value(field).value)
+        self.assertEqual("test-tracked", subject.provenances[0].modified_field)
+        self.assertEqual('BLA', subject.provenances[0].previous_value)
+        self.assertEqual('BLABLA', subject.provenances[0].new_value)
+
+    def test_subjects_add_subject_with_multiple_custom_file_field_tracked(self):
+        field = CustomStudySubjectField.objects.create(name="test-tracked", study=self.study,
+                                                       type=CUSTOM_FIELD_TYPE_TEXT, tracked=True)
+        field2 = CustomStudySubjectField.objects.create(name="test-tracked2", study=self.study,
+                                                        type=CUSTOM_FIELD_TYPE_TEXT, tracked=True)
+        self.worker.roles.all()[0].permissions.add(Permission.objects.get(codename="add_subject"))
+        self.worker.save()
+        form_data = self.create_add_form_data_for_study_subject()
+
+        form_data["study_subject-" + get_study_subject_field_id(field)] = "BLA"
+        form_data["study_subject-" + get_study_subject_field_id(field2)] = "lorem"
+        response = self.client.post(reverse('web.views.subject_add', kwargs={'study_id': self.study.id}),
+                                    data=form_data)
+
+        self.assertEqual(response.status_code, 302)
+
+        subject = StudySubject.objects.all().order_by("-id")[0]
+
+        self.assertTrue('BLA' in subject.get_custom_data_value(field).value)
+        self.assertTrue('lorem' in subject.get_custom_data_value(field2).value)
+        self.assertTrue(len(subject.provenances) == 0)
+
+        form_data["study_subject-" + get_study_subject_field_id(field)] = "BLABLA"
+        form_data["study_subject-" + get_study_subject_field_id(field2)] = "loremlorem"
+        response = self.client.post(reverse('web.views.subject_edit', kwargs={'subject_id': subject.id}),
+                                    data=form_data)
+
+        self.assertEqual(response.status_code, 302)
+        self.assertTrue('BLABLA' in subject.get_custom_data_value(field).value)
+        self.assertEqual("test-tracked", subject.provenances[1].modified_field)
+        self.assertEqual('BLA', subject.provenances[1].previous_value)
+        self.assertEqual('BLABLA', subject.provenances[1].new_value)
+
+        self.assertTrue('loremlorem' in subject.get_custom_data_value(field2).value)
+        self.assertEqual("test-tracked2", subject.provenances[0].modified_field)
+        self.assertEqual('lorem', subject.provenances[0].previous_value)
+        self.assertEqual('loremlorem', subject.provenances[0].new_value)
+
+    def test_subjects_add_subject_with_custom_file_field_not_tracked(self):
+        field = CustomStudySubjectField.objects.create(name="test-tracked", study=self.study,
+                                                       type=CUSTOM_FIELD_TYPE_TEXT, tracked=False)
+        self.worker.roles.all()[0].permissions.add(Permission.objects.get(codename="add_subject"))
+        self.worker.save()
+        form_data = self.create_add_form_data_for_study_subject()
+
+        form_data["study_subject-" + get_study_subject_field_id(field)] = "BLA"
+        response = self.client.post(reverse('web.views.subject_add', kwargs={'study_id': self.study.id}),
+                                    data=form_data)
+
+        self.assertEqual(response.status_code, 302)
+
+        subject = StudySubject.objects.all().order_by("-id")[0]
+
+        self.assertTrue('BLA' in subject.get_custom_data_value(field).value)
+        self.assertTrue(len(subject.provenances) == 0)
+
+        form_data["study_subject-" + get_study_subject_field_id(field)] = "BLABLA"
+        response = self.client.post(reverse('web.views.subject_edit', kwargs={'subject_id': subject.id}),
+                                    data=form_data)
+
+        self.assertEqual(response.status_code, 302)
+        self.assertTrue(len(subject.provenances) == 0)
diff --git a/smash/web/views/subject.py b/smash/web/views/subject.py
index 841061253bfda93f033452f458bfe0a34165f131..f181b08d3e40c1376a29f5436a0af2b92e56c4bc 100644
--- a/smash/web/views/subject.py
+++ b/smash/web/views/subject.py
@@ -1,6 +1,5 @@
 # coding=utf-8
 import logging
-
 from django.contrib import messages
 from django.http import HttpRequest, HttpResponse
 from django.shortcuts import redirect, get_object_or_404
@@ -14,13 +13,10 @@ from .view_utils import WrappedView, wrap_response
 from ..forms import VisitDetailForm, SubjectAddForm, SubjectEditForm, StudySubjectAddForm, StudySubjectEditForm
 from ..models import StudySubject, MailTemplate, Worker, Study, Provenance, Subject
 from ..models.constants import GLOBAL_STUDY_ID, FILE_STORAGE
-from ..models.study_subject_list import (
-    SUBJECT_LIST_GENERIC,
-    SUBJECT_LIST_NO_VISIT,
-    SUBJECT_LIST_REQUIRE_CONTACT,
-    SUBJECT_LIST_VOUCHER_EXPIRY,
-    SUBJECT_LIST_CHOICES,
-)
+from ..models.custom_data import CustomStudySubjectValue
+from ..models.custom_data.custom_study_subject_field import get_study_subject_field_id
+from ..models.study_subject_list import SUBJECT_LIST_GENERIC, SUBJECT_LIST_NO_VISIT, SUBJECT_LIST_REQUIRE_CONTACT, \
+    SUBJECT_LIST_VOUCHER_EXPIRY, SUBJECT_LIST_CHOICES
 from ..utils import get_client_ip
 
 logger = logging.getLogger(__name__)
@@ -139,22 +135,22 @@ def subject_edit(request, subject_id):
     old_type = study_subject.type
     endpoint_was_reached = study_subject.endpoint_reached
     ip = get_client_ip(request)
-    if request.method == "POST":
-        study_subject_form = StudySubjectEditForm(
-            request.POST,
-            request.FILES,
-            instance=study_subject,
-            was_resigned=was_resigned,
-            prefix="study_subject",
-            endpoint_was_reached=endpoint_was_reached,
-        )
-        subject_form = SubjectEditForm(
-            request.POST, request.FILES, instance=study_subject.subject, was_dead=was_dead, prefix="subject"
-        )
+
+    # save previous values of custom fields
+    prev_value_list_custom_fields = []
+    for field in CustomStudySubjectValue.objects.filter(study_subject=study_subject):
+        prev_value_list_custom_fields.append(field.value)
+
+    if request.method == 'POST':
+        study_subject_form = StudySubjectEditForm(request.POST, request.FILES, instance=study_subject,
+                                                  was_resigned=was_resigned, prefix="study_subject",
+                                                  endpoint_was_reached=endpoint_was_reached)
+        subject_form = SubjectEditForm(request.POST, request.FILES, instance=study_subject.subject,
+                                       was_dead=was_dead, prefix="subject"
+                                       )
         if study_subject_form.is_valid() and subject_form.is_valid():
             study_subject_form.save()
             subject_form.save()
-
             persist_custom_file_fields(request, study_subject)
 
             if "type" in study_subject_form.changed_data and old_type != study_subject_form.cleaned_data["type"]:
@@ -213,6 +209,30 @@ def subject_edit(request, subject_id):
                 )
                 study_subject.mark_as_resigned()
                 p.save()
+
+            # loop over custom fields to add Provenance to tracked custom fields
+            for field in CustomStudySubjectValue.objects.filter(study_subject=study_subject):
+                field_id = get_study_subject_field_id(field.study_subject_field)
+                prev_value = prev_value_list_custom_fields.pop(0)
+                if field_id in study_subject_form.changed_data and field.study_subject_field.tracked:
+                    curr_value = study_subject_form.cleaned_data[field_id]
+                    if prev_value != curr_value:
+                        worker = Worker.get_by_user(request.user)
+                        p = Provenance(modified_table=StudySubject._meta.db_table,
+                                       modified_table_id=study_subject.id,
+                                       modification_author=worker,
+                                       previous_value=prev_value,
+                                       new_value=curr_value,
+                                       modification_description='Worker "{}" changed study subject "{}" field "{}" '
+                                                                'value from study "{}"'
+                                       .format(worker, study_subject.nd_number, field.study_subject_field.name,
+                                               study_subject.study),
+                                       modified_field=field.study_subject_field.name,
+                                       request_path=request.path,
+                                       request_ip_addr=ip
+                                       )
+                        p.save()
+
             messages.success(request, "Modifications saved")
             if "_continue" in request.POST:
                 return redirect("web.views.subject_edit", subject_id=study_subject.id)