diff --git a/smash/web/forms/appointment_form.py b/smash/web/forms/appointment_form.py
index a07294741c65d29d1482b27e4f1cce1d4f2a72e9..d5ace225b009eee19ba16c3f1f029e8ffc26e10a 100644
--- a/smash/web/forms/appointment_form.py
+++ b/smash/web/forms/appointment_form.py
@@ -6,8 +6,9 @@ from django.forms import ModelForm
 
 from web.models.worker_study_role import WORKER_STAFF
 from web.forms.forms import DATETIMEPICKER_DATE_ATTRS, APPOINTMENT_TYPES_FIELD_POSITION
-from web.models import Appointment, Worker, AppointmentTypeLink, AppointmentType
+from web.models import Appointment, Worker, AppointmentTypeLink, AppointmentType, Provenance
 from web.views.notifications import get_filter_locations
+from django.db.models.query import QuerySet
 
 logger = logging.getLogger(__name__)
 
@@ -22,6 +23,46 @@ class AppointmentForm(ModelForm):
         self.fields['worker_assigned'].queryset = Worker.get_workers_by_worker_type(WORKER_STAFF).filter(
             locations__in=get_filter_locations(self.user)).distinct().order_by('first_name', 'last_name')
 
+    def save_changes(self):
+        for change in self.changes:
+            if change.modified_table_id is None:
+                change.modified_table_id = self.instance.id
+            change.save()
+
+    def register_changes(self):
+        self.changes = []
+        for field in self.changed_data:
+            new_value = self.cleaned_data[field]
+            if isinstance(new_value, QuerySet):
+                new_human_values = '; '.join([str(element) for element in new_value]) 
+                new_value = ','.join([str(element.id) for element in new_value]) #overwrite variable
+                #old value
+                if self.instance.id: #update instance
+                    previous_value = getattr(self.instance, field).all()
+                    old_human_values = '; '.join([str(element) for element in previous_value]) 
+                    previous_value = ','.join([str(element.id) for element in previous_value]) #overwrite variable
+                else: #new instance
+                    old_human_values = ''
+                    previous_value = ''
+                #description
+                description = '{} changed from "{}" to "{}"'.format(field, old_human_values, new_human_values)
+            else:
+                if self.instance.id: #update instance
+                    previous_value = str(getattr(self.instance, field))
+                else:
+                    previous_value = ''
+                new_value = str(self.cleaned_data[field])
+                description = '{} changed from "{}" to "{}"'.format(field, previous_value, new_value)
+  
+            p = Provenance(modified_table = Appointment._meta.db_table,
+                            modified_table_id = self.instance.id,
+                            modification_author=self.user,
+                            previous_value = previous_value,
+                            new_value = new_value,
+                            modification_description = description,
+                            modified_field = field,
+                            )
+            self.changes.append(p)
 
 class AppointmentDetailForm(AppointmentForm):
     class Meta:
@@ -67,7 +108,11 @@ class AppointmentEditForm(AppointmentForm):
         else:
             return location
 
+    def clean(self):
+        self.register_changes() #right before instance is changed
+
     def save(self, commit=True):
+
         appointment = super(AppointmentEditForm, self).save(commit)
         # if appointment date change, remove appointment_type links
         if 'datetime_when' in self.changed_data:
@@ -89,6 +134,8 @@ class AppointmentEditForm(AppointmentForm):
                 appointment_type_link = AppointmentTypeLink(appointment=appointment,
                                                             appointment_type=appointment_type)
                 appointment_type_link.save()
+        
+        self.save_changes()
         return appointment
 
 
@@ -123,6 +170,9 @@ class AppointmentAddForm(AppointmentForm):
         self.fields = fields
         self.fields['location'].queryset = get_filter_locations(self.user)
 
+    def clean(self):
+        self.register_changes() #right before instance is changed
+
     def clean_location(self):
         location = self.cleaned_data['location']
         if self.user.locations.filter(id=location.id).count() == 0:
@@ -136,4 +186,5 @@ class AppointmentAddForm(AppointmentForm):
         for appointment_type in appointment_types:
             appointment_type_link = AppointmentTypeLink(appointment=appointment, appointment_type=appointment_type)
             appointment_type_link.save()
+        self.save_changes()
         return appointment
diff --git a/smash/web/views/appointment.py b/smash/web/views/appointment.py
index d2d1f0ff92fa75353f7e8a08845270c45c67908e..84b12b2493f97f22c0e1073588c785815f2b515d 100644
--- a/smash/web/views/appointment.py
+++ b/smash/web/views/appointment.py
@@ -12,7 +12,7 @@ from web.models.appointment_list import APPOINTMENT_LIST_APPROACHING, APPOINTMEN
 from . import wrap_response
 from web.forms import AppointmentDetailForm, AppointmentEditForm, AppointmentAddForm, SubjectEditForm, \
     StudySubjectEditForm
-from ..models import Appointment, StudySubject, MailTemplate, Visit, Study
+from ..models import Appointment, StudySubject, MailTemplate, Visit, Study, Provenance, Worker
 from django.views.generic import DeleteView
 from . import WrappedView
 from django.urls import reverse_lazy
@@ -184,6 +184,16 @@ class AppointmentDeleteView(DeleteView, WrappedView):
             return redirect('web.views.appointment_edit', id=appointment.id)
         else:
             messages.success(request, "Appointment deleted")
+            worker = Worker.get_by_user(request.user)
+            p = Provenance(modified_table = Appointment._meta.db_table,
+                            modified_table_id = appointment.id,
+                            modification_author = worker,
+                            previous_value = '',
+                            new_value = '',
+                            modification_description = 'Appointment deleted',
+                            modified_field = '',
+                            )
+            p.save()
             return super(AppointmentDeleteView, self).delete(request, *args, **kwargs)
 
     @PermissionDecorator('delete_appointment', 'configuration')