diff --git a/SAS/TMSS/src/tmss/tmssapp/migrations/0001_initial.py b/SAS/TMSS/src/tmss/tmssapp/migrations/0001_initial.py
index 48c2c95468c4997e165fa571384ac559cae5309d..7a630f07320fc7a2fda98f04615255a7ed98dd3b 100644
--- a/SAS/TMSS/src/tmss/tmssapp/migrations/0001_initial.py
+++ b/SAS/TMSS/src/tmss/tmssapp/migrations/0001_initial.py
@@ -1,4 +1,4 @@
-# Generated by Django 3.0.9 on 2020-10-14 16:37
+# Generated by Django 3.0.9 on 2020-10-14 18:48
 
 from django.conf import settings
 import django.contrib.postgres.fields
@@ -1267,6 +1267,11 @@ class Migration(migrations.Migration):
             name='dataproduct',
             field=models.ForeignKey(help_text='A dataproduct residing in the archive.', on_delete=django.db.models.deletion.PROTECT, to='tmssapp.Dataproduct'),
         ),
+        migrations.AddField(
+            model_name='dataproduct',
+            name='SAP',
+            field=models.ForeignKey(help_text='SAP this dataproduct was generated out of (NULLable).', null=True, on_delete=django.db.models.deletion.PROTECT, related_name='SAP_dataproducts', to='tmssapp.SAP'),
+        ),
         migrations.AddField(
             model_name='dataproduct',
             name='dataformat',
diff --git a/SAS/TMSS/src/tmss/tmssapp/models/scheduling.py b/SAS/TMSS/src/tmss/tmssapp/models/scheduling.py
index 8a65ffcce93882908ce82990ebeb8a56bb3a5ff2..0d9532f288902cfcd1fcc4345f6634f02e16f968 100644
--- a/SAS/TMSS/src/tmss/tmssapp/models/scheduling.py
+++ b/SAS/TMSS/src/tmss/tmssapp/models/scheduling.py
@@ -257,11 +257,13 @@ class SubtaskInput(BasicCommon):
 class SubtaskOutput(BasicCommon):
     subtask = ForeignKey('Subtask', null=False, on_delete=CASCADE, related_name='outputs', help_text='Subtask to which this output specification refers.')
 
+
 class SAP(BasicCommon):
-    name =  CharField(max_length=128, unique=True)  # todo: we also have NamedCommon, which would make this PK. Confluence is a little ambiguous whether id or name...
-    specifications_doc =  JSONField(help_text='SAP properties.')
+    name = CharField(max_length=128, unique=True)  # todo: we also have NamedCommon, which would make this PK. Confluence is a little ambiguous whether id or name...
+    specifications_doc = JSONField(help_text='SAP properties.')
     specifications_template = ForeignKey('SAPTemplate', null=False, on_delete=CASCADE, help_text='Schema used for specifications_doc.')
 
+
 class Dataproduct(BasicCommon):
     """
     A data product represents an atomic dataset, produced and consumed by subtasks. The consumed dataproducts are those
@@ -283,6 +285,7 @@ class Dataproduct(BasicCommon):
     size = BigIntegerField(null=True, help_text='Dataproduct size, in bytes. Used for accounting purposes. NULL if size is (yet) unknown (NULLable).')
     feedback_doc = JSONField(help_text='Dataproduct properties, as reported by the producing process.')
     feedback_template = ForeignKey('DataproductFeedbackTemplate', on_delete=PROTECT, help_text='Schema used for feedback_doc.')
+    SAP = ForeignKey('SAP', on_delete=PROTECT, null=True, related_name="SAP_dataproducts", help_text='SAP this dataproduct was generated out of (NULLable).')  # todo: I find it odd to have this uppercase
 
     def save(self, force_insert=False, force_update=False, using=None, update_fields=None):
         annotate_validate_add_defaults_to_doc_using_template(self, 'specifications_doc', 'specifications_template')
diff --git a/SAS/TMSS/test/t_tmssapp_scheduling_django_API.py b/SAS/TMSS/test/t_tmssapp_scheduling_django_API.py
index ec2a1bb407b065247fa6a087618968ac0606bdfc..f5207337417bb58a4b825451ff3fe63437f9a9ea 100755
--- a/SAS/TMSS/test/t_tmssapp_scheduling_django_API.py
+++ b/SAS/TMSS/test/t_tmssapp_scheduling_django_API.py
@@ -472,6 +472,69 @@ class DataproductHashTest(unittest.TestCase):
         self.assertGreater(after, entry.updated_at)
 
 
+class SAPTemplateTest(unittest.TestCase):
+    def test_SAPTemplate_gets_created_with_correct_creation_timestamp(self):
+
+        # setup
+        before = datetime.utcnow()
+        entry = models.SAPTemplate.objects.create(**SAPTemplate_test_data())
+
+        after = datetime.utcnow()
+
+        # assert
+        self.assertLess(before, entry.created_at)
+        self.assertGreater(after, entry.created_at)
+
+    def test_SAPTemplate_update_timestamp_gets_changed_correctly(self):
+
+        # setup
+        entry = models.SAPTemplate.objects.create(**SAPTemplate_test_data())
+        before = datetime.utcnow()
+        entry.save()
+        after = datetime.utcnow()
+
+        # assert
+        self.assertLess(before, entry.updated_at)
+        self.assertGreater(after, entry.updated_at)
+
+
+class SAPTest(unittest.TestCase):
+    def test_SAP_gets_created_with_correct_creation_timestamp(self):
+
+        # setup
+        before = datetime.utcnow()
+        entry = models.SAP.objects.create(**SAP_test_data())
+
+        after = datetime.utcnow()
+
+        # assert
+        self.assertLess(before, entry.created_at)
+        self.assertGreater(after, entry.created_at)
+
+    def test_SAP_update_timestamp_gets_changed_correctly(self):
+
+        # setup
+        entry = models.SAP.objects.create(**SAP_test_data())
+        before = datetime.utcnow()
+        entry.save()
+        after = datetime.utcnow()
+
+        # assert
+        self.assertLess(before, entry.updated_at)
+        self.assertGreater(after, entry.updated_at)
+
+
+    def test_SAP_prevents_missing_specifications_template(self):
+
+        # setup
+        test_data = dict(SAP_test_data())
+        test_data['specifications_template'] = None
+
+        # assert
+        with self.assertRaises(IntegrityError):
+            models.SAP.objects.create(**test_data)
+
+
 if __name__ == "__main__":
     os.environ['TZ'] = 'UTC'
     unittest.main()
diff --git a/SAS/TMSS/test/tmss_test_data_django_models.py b/SAS/TMSS/test/tmss_test_data_django_models.py
index 13fc9c26cb698e3bbb65b2f4152d276c20cb3b55..e171076c313a8d7171a5b5f847157022e9838f3e 100644
--- a/SAS/TMSS/test/tmss_test_data_django_models.py
+++ b/SAS/TMSS/test/tmss_test_data_django_models.py
@@ -466,3 +466,25 @@ def DataproductHash_test_data() -> dict:
             "algorithm": models.Algorithm.objects.get(value='md5'),
             "hash": "myhash_1",
             "tags": ['tmss', 'testing']}
+
+
+def SAP_test_data(specifications_template=None, specifications_doc=None) -> dict:
+
+    if specifications_template is None:
+        specifications_template = models.SAPTemplate.objects.create(**SAPTemplate_test_data())
+
+    if specifications_doc is None:
+        specifications_doc = get_default_json_object_for_schema(specifications_template.schema)
+
+    return {"name": "my_sap" + str(uuid.uuid4()),
+            "specifications_doc": specifications_doc,
+            "specifications_template": specifications_template,
+            "tags": ['tmss', 'testing']}
+
+
+def SAPTemplate_test_data() -> dict:
+    return {"name": "my_sap_template" + str(uuid.uuid4()),
+            "description": 'My SAP test template',
+            "schema": minimal_json_schema(),
+            "tags": ["TMSS", "TESTING"]}
+
diff --git a/SAS/TMSS/test/tmss_test_data_rest.py b/SAS/TMSS/test/tmss_test_data_rest.py
index 76f23608e242dd7efed4380290729249c999c476..9b8c4c4c30fb082c6872d4e56863768322987878 100644
--- a/SAS/TMSS/test/tmss_test_data_rest.py
+++ b/SAS/TMSS/test/tmss_test_data_rest.py
@@ -534,7 +534,9 @@ class TMSSRESTTestDataCreator():
                     specifications_doc=None, specifications_template_url=None,
                     subtask_output_url=None,
                     dataproduct_feedback_doc=None, dataproduct_feedback_template_url=None,
-                    dataformat="MeasurementSet", datatype="visibilities"):
+                    dataformat="MeasurementSet", datatype="visibilities",
+                    sap_url=None):
+
         if specifications_template_url is None:
             specifications_template_url = self.post_data_and_get_url(self.SubtaskTemplate(), '/dataproduct_specifications_template/')
     
@@ -550,6 +552,9 @@ class TMSSRESTTestDataCreator():
         if dataproduct_feedback_doc is None:
             dataproduct_feedback_doc = self.get_response_as_json_object(dataproduct_feedback_template_url+'/default')
 
+        if sap_url is None:
+            sap_url = self.post_data_and_get_url(self.SAP(), '/sap/')
+
         return {"filename": filename,
                 "directory": directory,
                 "dataformat": "%s/dataformat/%s" % (self.django_api_url, dataformat),
@@ -564,7 +569,8 @@ class TMSSRESTTestDataCreator():
                 "expected_size": 1234,
                 "size": 123,
                 "feedback_doc": dataproduct_feedback_doc,
-                "feedback_template": dataproduct_feedback_template_url
+                "feedback_template": dataproduct_feedback_template_url,
+                "SAP": sap_url
                 }
     
     def AntennaSet(self, name="antennaset1"):
@@ -648,4 +654,22 @@ class TMSSRESTTestDataCreator():
                 "cluster": cluster_url,
                 "directory": '/',
                 "tags": ['tmss', 'testing']}
-    
+
+    def SAPTemplate(self):
+        return {"name": "my_sap_template" + str(uuid.uuid4()),
+                "description": 'My SAP test template',
+                "schema": minimal_json_schema(),
+                "tags": ["TMSS", "TESTING"]}
+
+    def SAP(self, specifications_template_url=None, specifications_doc=None):
+
+        if specifications_template_url is None:
+            specifications_template_url = self.post_data_and_get_url(self.SAPTemplate(), '/sap_template/')
+
+        if specifications_doc is None:
+            specifications_doc = self.get_response_as_json_object(specifications_template_url + '/default')
+
+        return {"name": "my_sap" + str(uuid.uuid4()),
+                "specifications_doc": specifications_doc,
+                "specifications_template": specifications_template_url,
+                "tags": ['tmss', 'testing']}
\ No newline at end of file