diff --git a/arkindex/process/api.py b/arkindex/process/api.py
index 386a1119103dbc93a293c89997dbbedd157e5f41..cd56db41de1ab8ad1f566d67997a46e14f68f8a9 100644
--- a/arkindex/process/api.py
+++ b/arkindex/process/api.py
@@ -920,7 +920,9 @@ class WorkerTypesList(ListAPIView):
         description=dedent("""
             Create a new version for a worker.
 
-            The user must have an administrator access to the worker.
+            - The user must have an administrator access to the worker.
+            - If the worker has a `repository_url` set, then the version must be created with a `revision_url`.
+            - Worker versions created via the Ponos task authentication must have a `revision_url`.
         """)
     )
 )
diff --git a/arkindex/process/managers.py b/arkindex/process/managers.py
index 3abe706ed374ad400d1b325e529d32d0d386d428..c7ca82b2d00d2fd1e91b162e86a5013a7543203a 100644
--- a/arkindex/process/managers.py
+++ b/arkindex/process/managers.py
@@ -193,7 +193,6 @@ class WorkerVersionManager(Manager):
             self
             # Required by WorkerRun.build_summary
             .select_related("worker", "revision")
-            .prefetch_related("revision__refs")
             .get(id=settings.IMPORTS_WORKER_VERSION)
         )
 
diff --git a/arkindex/process/migrations/0040_worker_repository_url_workerversion_revision_url_and_more.py b/arkindex/process/migrations/0040_worker_repository_url_workerversion_revision_url_and_more.py
new file mode 100644
index 0000000000000000000000000000000000000000..6b6d09d77d2e51310174c48325c4025f7a78d3e7
--- /dev/null
+++ b/arkindex/process/migrations/0040_worker_repository_url_workerversion_revision_url_and_more.py
@@ -0,0 +1,82 @@
+# Generated by Django 5.0.6 on 2024-07-11 14:52
+
+from django.db import migrations, models
+from django.db.models import CharField, F, OuterRef, Subquery, Value
+from django.db.models.functions import Concat
+
+from arkindex.process.models import GitRefType
+from arkindex.project.tools import RTrimChr
+
+
+def update_git_refs(apps, schema):
+    Worker = apps.get_model("process", "Worker")
+    WorkerVersion = apps.get_model("process", "WorkerVersion")
+
+    Worker.objects.update(
+        repository_url=Subquery(
+            Worker.objects
+            .filter(pk=OuterRef("pk"))
+            .values("repository__url")
+        )
+    )
+
+    WorkerVersion.objects.update(
+        revision_url=Subquery(
+            WorkerVersion.objects
+            .filter(pk=OuterRef("pk"))
+            .annotate(
+                rev_url=Concat(
+                    RTrimChr("worker__repository__url", Value("/")),
+                    Value("/commit/"),
+                    F("revision__hash"),
+                    output_field=CharField(),
+                )
+            )
+            .values("rev_url")
+        ),
+        tag=Subquery(
+            WorkerVersion.objects
+            .filter(
+                pk=OuterRef("pk"),
+                revision__refs__type=GitRefType.Tag,
+            )
+            .order_by("-revision__refs__name")
+            [:1]
+            .values("revision__refs__name")
+        )
+    )
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ("documents", "0012_alter_transcriptionentity_id"),
+        ("process", "0039_worker_configuration_name_not_empty"),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name="worker",
+            name="repository_url",
+            field=models.URLField(blank=True, default=None, max_length=250, null=True),
+        ),
+        migrations.AddField(
+            model_name="workerversion",
+            name="revision_url",
+            field=models.URLField(blank=True, default=None, max_length=250, null=True),
+        ),
+        migrations.AddField(
+            model_name="workerversion",
+            name="tag",
+            field=models.CharField(blank=True, default=None, max_length=50, null=True),
+        ),
+        migrations.AddConstraint(
+            model_name="workerversion",
+            constraint=models.UniqueConstraint(condition=models.Q(("tag__isnull", False)), fields=("worker", "tag"), name="workerversion_unique_tag"),
+        ),
+        migrations.RunPython(
+            update_git_refs,
+            reverse_code=migrations.RunPython.noop,
+            elidable=True,
+        ),
+    ]
diff --git a/arkindex/process/models.py b/arkindex/process/models.py
index d9932105751a09889d8101c41d1d60731cb594e6..379de8af11d9dca7d2148f84cf905ca5cd2f9292 100644
--- a/arkindex/process/models.py
+++ b/arkindex/process/models.py
@@ -610,6 +610,8 @@ class Worker(models.Model):
 
     archived = models.DateTimeField(null=True, blank=True, default=None)
 
+    repository_url = models.URLField(null=True, blank=True, max_length=250, default=None)
+
     objects = WorkerManager()
 
     class Meta:
@@ -758,6 +760,10 @@ class WorkerVersion(models.Model):
     # For workers imported with `arkindex workers publish`, the tag of the Docker image
     docker_image_iid = models.CharField(null=True, blank=True, max_length=512)
 
+    # URL of the commit for this version, when worker is based on a repository
+    revision_url = models.URLField(null=True, blank=True, max_length=250, default=None)
+    tag = models.CharField(blank=True, null=True, max_length=50, default=None)
+
     corpora = models.ManyToManyField(
         "documents.Corpus",
         through="process.CorpusWorkerVersion",
@@ -794,6 +800,11 @@ class WorkerVersion(models.Model):
                 name="workerversion_unique_version",
                 condition=Q(version__isnull=False),
             ),
+            models.UniqueConstraint(
+                fields=["worker", "tag"],
+                name="workerversion_unique_tag",
+                condition=Q(tag__isnull=False),
+            ),
         ]
 
     def __str__(self):
@@ -1002,14 +1013,17 @@ class WorkerRun(models.Model):
         """
 
         summary_text = f"Worker {self.version.worker.name} @ "
-        if self.version.version is not None:
-            summary_text += f"version {self.version.version}"
-        else:
-            git_ref_names = self.version.revision.refs.values_list("name", flat=True)
-            if len(git_ref_names) > 0:
-                summary_text += ", ".join(git_ref_names)
+        if self.version.tag is not None:
+            summary_text += f"{self.version.tag} "
+            if self.version.revision is not None:
+                summary_text += f"({self.version.truncated_id})"
             else:
+                summary_text += f"(version {self.version.version})"
+        else:
+            if self.version.revision is not None:
                 summary_text += self.version.truncated_id
+            else:
+                summary_text += f"version {self.version.version}"
 
         if self.model_version:
             summary_text += f" with model {self.model_version.model.name} @ {self.model_version.truncated_id}"
diff --git a/arkindex/process/serializers/workers.py b/arkindex/process/serializers/workers.py
index 46b64548cc41971616e00cfe7919e1d49cfd8b35..a0f7a348c33b76da68278ca664de2bb032fe539d 100644
--- a/arkindex/process/serializers/workers.py
+++ b/arkindex/process/serializers/workers.py
@@ -55,7 +55,7 @@ class WorkerSerializer(WorkerLightSerializer):
     archived = ArchivedField(required=False)
 
     class Meta(WorkerLightSerializer.Meta):
-        fields = ("id", "name", "description", "type", "slug", "repository_id", "archived")
+        fields = ("id", "name", "description", "type", "slug", "repository_id", "repository_url", "archived")
         read_only_fields = ("id", "repository_id")
         extra_kwargs = {
             "description": {
@@ -78,6 +78,7 @@ class WorkerCreateSerializer(WorkerSerializer):
 
         If the WorkerType does not exist, it is created automatically.
     """))
+    repository_url = serializers.URLField(allow_null=True, required=False)
 
     # Creating an archived worker makes no sense
     archived = ArchivedField(read_only=True)
@@ -115,6 +116,7 @@ class WorkerCreateSerializer(WorkerSerializer):
                 slug=validated_data["slug"],
                 name=validated_data["name"],
                 type=worker_type,
+                repository_url=validated_data.get("repository_url")
             )
             # Automatically grant an admin access to the creator of a local worker
             worker.memberships.create(user=request.user, level=Role.Admin.value)
@@ -231,10 +233,12 @@ class WorkerVersionSerializer(serializers.ModelSerializer):
     state = EnumField(WorkerVersionState, required=False)
     worker = WorkerLightSerializer(read_only=True)
     revision = RevisionWithRefsSerializer(required=False, read_only=True, allow_null=True)
+    revision_url = serializers.URLField(required=False, read_only=True, allow_null=True)
     gpu_usage = EnumField(FeatureUsage, required=False, default=FeatureUsage.Disabled)
     model_usage = EnumField(FeatureUsage, required=False, default=FeatureUsage.Disabled)
     # Ensure worker version configuration JSON body is an object
     configuration = serializers.DictField()
+    tag = serializers.CharField(allow_null=True, max_length=50, required=False)
 
     # Serialize worker with its basic information
     class Meta:
@@ -243,6 +247,7 @@ class WorkerVersionSerializer(serializers.ModelSerializer):
             "id",
             "configuration",
             "revision",
+            "revision_url",
             "version",
             "docker_image_iid",
             "state",
@@ -250,13 +255,21 @@ class WorkerVersionSerializer(serializers.ModelSerializer):
             "model_usage",
             "worker",
             "created",
+            "tag",
         )
-        read_only_fields = ("revision", "version")
+        read_only_fields = ("revision", "revision_url", "version")
         # Avoid loading all revisions and all Ponos artifacts when opening this endpoint in a browser
         extra_kwargs = {
             "revision": {"style": {"base_template": "input.html"}},
         }
 
+    def validate_tag(self, tag):
+        worker = self.instance.worker if self.instance else self.context["worker"]
+        existing_version = worker.versions.filter(tag=tag).exists()
+        if existing_version:
+            raise ValidationError("A version already exists for this worker with this tag.")
+        return tag
+
     def validate_configuration(self, configuration):
         errors = defaultdict(list)
         user_configuration = configuration.get("user_configuration")
@@ -290,46 +303,11 @@ class WorkerVersionSerializer(serializers.ModelSerializer):
 
 
 class WorkerVersionCreateSerializer(WorkerVersionSerializer):
-    revision_id = serializers.PrimaryKeyRelatedField(
+    revision_url = serializers.URLField(
         required=False,
-        write_only=True,
-        queryset=Revision.objects.all(),
-        allow_null=True,
-        style={"base_template": "input.html"},
-        help_text=dedent("""
-            Git revision for this version.
-
-            This field is required on workers linked to a repository.
-            On other workers, it cannot be set as an automatic version number is attributed.
-        """).strip(),
-        source="revision"
+        allow_null=True
     )
 
-    class Meta (WorkerVersionSerializer.Meta):
-        fields = WorkerVersionSerializer.Meta.fields + (
-            "revision_id",
-        )
-
-    def validate_revision_id(self, revision):
-        worker = self.context["worker"]
-        if worker.versions.using("default").filter(revision=revision).exists():
-            raise ValidationError("A version of this worker already exists with this revision")
-        if isinstance(self.context["request"].auth, Task):
-            if worker.repository_id is None:
-                # Task authentication is forbidden on workers not linked to a repository
-                raise ValidationError(
-                    "Task authentication requires to create a version on workers linked to a repository."
-                )
-        elif worker.repository_id is not None:
-            raise ValidationError(
-                "Ponos authentication is required to create a version on a worker linked to a repository."
-            )
-        if worker.repository is None:
-            raise ValidationError("A revision cannot be set on a worker that is not linked to a repository.")
-        if worker.repository_id != revision.repo_id:
-            raise ValidationError("The revision must be part of the same repository as the worker.")
-        return revision
-
     def validate_docker_image_iid(self, docker_image_iid):
         repository, _ = parse_repository_tag(docker_image_iid)
         try:
@@ -346,19 +324,19 @@ class WorkerVersionCreateSerializer(WorkerVersionSerializer):
         if worker.archived:
             errors["worker"].append("This worker is archived.")
 
-        revision = data.get("revision")
-        if revision is None and worker.repository_id is not None:
-            errors["revision_id"].append("A revision must be set on a worker that is linked to a repository.")
+        revision_url = data.get("revision_url")
+        if isinstance(self.context["request"].auth, Task) and not revision_url:
+            errors["revision_url"].append("This field is required when creating a version through Task authentication.")
+        if worker.repository_url and not revision_url:
+            errors["revision_url"].append("A revision url is required when creating a version for a worker linked to a repository.")
 
         if errors:
             raise ValidationError(errors)
 
-        # Define a version number on workers that are not linked to a repository
-        version = None
-        if revision is None:
-            last_version = worker.versions.using("default").aggregate(last_version=Max("version"))["last_version"]
-            version = last_version + 1 if last_version else 1
-        data["version"] = version
+        # Define a version number
+        last_version = worker.versions.using("default").aggregate(last_version=Max("version"))["last_version"]
+        data["version"] = last_version + 1 if last_version else 1
+
         data["worker_id"] = worker.id
         return data
 
diff --git a/arkindex/process/tests/test_corpus_worker_runs.py b/arkindex/process/tests/test_corpus_worker_runs.py
index 7426372bb790e351ee655448d77d4048d04886b4..fa5d1f8184ea9044ac0cae9ddb26bf699a886550 100644
--- a/arkindex/process/tests/test_corpus_worker_runs.py
+++ b/arkindex/process/tests/test_corpus_worker_runs.py
@@ -126,8 +126,10 @@ class TestCorpusWorkerRuns(FixtureAPITestCase):
                     "id": str(self.local_worker_version.id),
                     "model_usage": FeatureUsage.Disabled.value,
                     "revision": None,
+                    "revision_url": None,
                     "state": "created",
                     "version": 1,
+                    "tag": None,
                     "worker": {
                         "id": str(self.local_worker_version.worker.id),
                         "name": "Custom worker",
@@ -146,7 +148,9 @@ class TestCorpusWorkerRuns(FixtureAPITestCase):
                     "gpu_usage": "disabled",
                     "model_usage": FeatureUsage.Disabled.value,
                     "revision": None,
+                    "revision_url": None,
                     "version": 1,
+                    "tag": None,
                     "created": self.dla_worker_version.created.isoformat().replace("+00:00", "Z"),
                     "state": "available",
                     "worker": {
diff --git a/arkindex/process/tests/test_docker_worker_version.py b/arkindex/process/tests/test_docker_worker_version.py
index 97831efbe1e5d2cc86fd5ac59f8c36e5d9a3b0db..08ea29e1fa995188bdbe739902b26e63df03dfcd 100644
--- a/arkindex/process/tests/test_docker_worker_version.py
+++ b/arkindex/process/tests/test_docker_worker_version.py
@@ -267,7 +267,9 @@ class TestDockerWorkerVersion(FixtureAPITestCase):
                 "message": "created from docker image",
                 "refs": []
             },
+            "revision_url": None,
             "version": None,
+            "tag": None,
             "created": new_version.created.isoformat().replace("+00:00", "Z"),
             "state": "available",
             "worker": {
@@ -332,7 +334,9 @@ class TestDockerWorkerVersion(FixtureAPITestCase):
                     for ref in refs
                 ],
             },
+            "revision_url": None,
             "version": None,
+            "tag": None,
             "created": new_version.created.isoformat().replace("+00:00", "Z"),
             "state": "available",
             "worker": {
@@ -403,7 +407,9 @@ class TestDockerWorkerVersion(FixtureAPITestCase):
                     for ref in refs
                 ],
             },
+            "revision_url": None,
             "version": None,
+            "tag": None,
             "created": new_version.created.isoformat().replace("+00:00", "Z"),
             "state": "available",
             "worker": {
@@ -499,7 +505,9 @@ class TestDockerWorkerVersion(FixtureAPITestCase):
                 "hash": "new_revision_hash",
                 "message": "Bruce was very clever",
             },
+            "revision_url": None,
             "version": None,
+            "tag": None,
             "created": new_version.created.isoformat().replace("+00:00", "Z"),
             "state": "available",
             "worker": {
@@ -572,7 +580,9 @@ class TestDockerWorkerVersion(FixtureAPITestCase):
                     },
                 ],
             },
+            "revision_url": None,
             "version": None,
+            "tag": None,
             "created": self.version.created.isoformat().replace("+00:00", "Z"),
             "state": "available",
             "worker": {
@@ -629,7 +639,9 @@ class TestDockerWorkerVersion(FixtureAPITestCase):
                 "refs": [{"id": str(new_ref.id), "name": new_ref.name, "type": new_ref.type.value}],
             },
             "state": "available",
+            "revision_url": None,
             "version": None,
+            "tag": None,
             "created": new_version.created.isoformat().replace("+00:00", "Z"),
             "worker": {
                 "id": str(new_version.worker.id),
diff --git a/arkindex/process/tests/test_user_workerruns.py b/arkindex/process/tests/test_user_workerruns.py
index 2b14a8300f4310c6eadade9e5796305607647334..4a76ae878d299d830502ce595560fdc321387c6a 100644
--- a/arkindex/process/tests/test_user_workerruns.py
+++ b/arkindex/process/tests/test_user_workerruns.py
@@ -101,8 +101,10 @@ class TestUserWorkerRuns(FixtureAPITestCase):
                 "id": str(self.version_1.id),
                 "model_usage": FeatureUsage.Disabled.value,
                 "revision": None,
+                "revision_url": None,
                 "state": "available",
                 "version": 1,
+                "tag": None,
                 "worker": {
                     "id": str(self.version_1.worker.id),
                     "name": "Recognizer",
@@ -137,8 +139,10 @@ class TestUserWorkerRuns(FixtureAPITestCase):
                 "id": str(self.custom_version.id),
                 "model_usage": FeatureUsage.Disabled.value,
                 "revision": None,
+                "revision_url": None,
                 "state": "created",
                 "version": 1,
+                "tag": None,
                 "worker": {
                     "id": str(self.custom_version.worker.id),
                     "name": "Custom worker",
@@ -218,8 +222,10 @@ class TestUserWorkerRuns(FixtureAPITestCase):
                 "id": str(self.other_version.id),
                 "model_usage": FeatureUsage.Disabled.value,
                 "revision": None,
+                "revision_url": None,
                 "state": "created",
                 "version": 2,
+                "tag": None,
                 "worker": {
                     "id": str(self.other_version.worker.id),
                     "name": "Custom worker",
@@ -433,8 +439,10 @@ class TestUserWorkerRuns(FixtureAPITestCase):
                 "id": str(self.other_version.id),
                 "model_usage": FeatureUsage.Disabled.value,
                 "revision": None,
+                "revision_url": None,
                 "state": "created",
                 "version": 2,
+                "tag": None,
                 "worker": {
                     "id": str(self.other_version.worker.id),
                     "name": "Custom worker",
diff --git a/arkindex/process/tests/test_workerruns.py b/arkindex/process/tests/test_workerruns.py
index 5ea79b7ac59bf29a2bd1633a1469764469ce95b4..47387dac7e63e0c2ffa3929b1813aa0187dabf7a 100644
--- a/arkindex/process/tests/test_workerruns.py
+++ b/arkindex/process/tests/test_workerruns.py
@@ -136,7 +136,9 @@ class TestWorkerRuns(FixtureAPITestCase):
                 "gpu_usage": "disabled",
                 "model_usage": FeatureUsage.Disabled.value,
                 "revision": None,
+                "revision_url": None,
                 "version": 1,
+                "tag": None,
                 "created": self.version_1.created.isoformat().replace("+00:00", "Z"),
                 "state": "available",
                 "worker": {
@@ -404,7 +406,9 @@ class TestWorkerRuns(FixtureAPITestCase):
                         "gpu_usage": "disabled",
                         "model_usage": FeatureUsage.Disabled.value,
                         "revision": None,
+                        "revision_url": None,
                         "version": 1,
+                        "tag": None,
                         "created": self.version_1.created.isoformat().replace("+00:00", "Z"),
                         "state": "available",
                         "worker": {
@@ -476,7 +480,9 @@ class TestWorkerRuns(FixtureAPITestCase):
                 "gpu_usage": "disabled",
                 "model_usage": FeatureUsage.Disabled.value,
                 "revision": None,
+                "revision_url": None,
                 "version": 1,
+                "tag": None,
                 "created": self.version_1.created.isoformat().replace("+00:00", "Z"),
                 "state": "available",
                 "worker": {
@@ -524,94 +530,79 @@ class TestWorkerRuns(FixtureAPITestCase):
 
         self.assertDictEqual(response.json(), {"configuration_id": ["The configuration must be part of the same worker."]})
 
-    def test_list_summary_with_git_refs(self):
-        """
-        Create a worker run with a worker version related to a revision tagged with a GitRef
-        """
+    def test_summary(self):
         self.client.force_login(self.user)
-
         repo = Repository.objects.create(url="http://nerv.co.jp/nerv/eva/")
-        # Version with no git refs
         revision = Revision.objects.create(
-            repo_id=repo.id,
-            hash=uuid.uuid4().hex,
-            message="Fake revision",
-            author="Teklia Bot"
+            repo=repo,
+            hash="1",
+            message="commit message",
+            author="bob",
         )
-        git_ref_names = ["main", "develop", "trunk", "master", "patate", "pouet"]
-        expected_refs = []
-        for git_ref_name in git_ref_names:
-            new_ref = revision.refs.create(
-                name=git_ref_name,
-                type=GitRefType.Branch,
-                repository=revision.repo
-            )
-            expected_refs.append({"id": str(new_ref.id), "name": git_ref_name, "type": "branch"})
-
-        version = WorkerVersion.objects.create(
-            worker=self.worker_1,
-            revision=revision,
-            configuration={},
+        test_version = self.worker_1.versions.create(
+            version=2,
             state=WorkerVersionState.Available,
-            docker_image_iid=self.version_1.docker_image_iid,
+            docker_image_iid="registry.gitlab.com/dead-sea-scrolls:004",
+            model_usage=FeatureUsage.Supported
         )
 
-        with self.assertNumQueries(8):
-            response = self.client.post(
-                reverse("api:worker-run-list", kwargs={"pk": str(self.process_2.id)}),
-                data={"worker_version_id": str(version.id), "parents": []},
-                format="json",
-            )
-            self.assertEqual(response.status_code, status.HTTP_201_CREATED)
+        cases = [
+            ("eva-01", revision, self.model_version_1, self.configuration_1, f"Worker Recognizer @ eva-01 ({str(test_version.id)[:6]}) with model My model @ {str(self.model_version_1.id)[:6]} using configuration 'My config'"),
+            (None, revision, self.model_version_1, self.configuration_1, f"Worker Recognizer @ {str(test_version.id)[:6]} with model My model @ {str(self.model_version_1.id)[:6]} using configuration 'My config'"),
+            ("eva-01", revision, None, self.configuration_1, f"Worker Recognizer @ eva-01 ({str(test_version.id)[:6]}) using configuration 'My config'"),
+            (None, revision, self.model_version_1, None, f"Worker Recognizer @ {str(test_version.id)[:6]} with model My model @ {str(self.model_version_1.id)[:6]}"),
+            ("eva-01", revision, None, None, f"Worker Recognizer @ eva-01 ({str(test_version.id)[:6]})"),
+            (None, revision, None, None, f"Worker Recognizer @ {str(test_version.id)[:6]}"),
+            ("eva-01", None, self.model_version_1, self.configuration_1, f"Worker Recognizer @ eva-01 (version 2) with model My model @ {str(self.model_version_1.id)[:6]} using configuration 'My config'"),
+            (None, None, self.model_version_1, self.configuration_1, f"Worker Recognizer @ version 2 with model My model @ {str(self.model_version_1.id)[:6]} using configuration 'My config'"),
+            ("eva-01", None, None, self.configuration_1, "Worker Recognizer @ eva-01 (version 2) using configuration 'My config'"),
+            (None, None, self.model_version_1, None, f"Worker Recognizer @ version 2 with model My model @ {str(self.model_version_1.id)[:6]}"),
+            ("eva-01", None, None, None, "Worker Recognizer @ eva-01 (version 2)"),
+            (None, None, None, None, "Worker Recognizer @ version 2"),
+        ]
 
-        data = response.json()
-        pk = data.pop("id")
-        self.assertNotEqual(pk, self.run_1.id)
-        self.assertDictEqual(data, {
-            "worker_version": {
-                "id": str(version.id),
-                "configuration": {},
-                "docker_image_iid": version.docker_image_iid,
-                "gpu_usage": "disabled",
-                "model_usage": FeatureUsage.Disabled.value,
-                "revision": {
-                    "id": str(version.revision.id),
-                    "author": "Teklia Bot",
-                    "commit_url": f"http://nerv.co.jp/nerv/eva/commit/{version.revision.hash}",
-                    "created": version.revision.created.isoformat().replace("+00:00", "Z"),
-                    "hash": version.revision.hash,
-                    "message": "Fake revision",
-                    "refs": expected_refs,
-                },
-                "version": None,
-                "created": version.created.isoformat().replace("+00:00", "Z"),
-                "state": "available",
-                "worker": {
-                    "id": str(self.worker_1.id),
-                    "name": "Recognizer",
-                    "slug": "reco",
-                    "type": "recognizer"
+        for tag, revision, model_version, config, expected_summary in cases:
+            with self.subTest(tag=tag, revision=revision, model_version=model_version, config=config):
+                # Clear the process of worker runs
+                self.process_2.worker_runs.all().delete()
+
+                num_queries = 6
+
+                test_version.tag = tag
+                test_version.revision = None
+                test_version.version = 2
+                if revision:
+                    test_version.revision = revision
+                    test_version.version = None
+                    # If there is a revision, it adds a query
+                    num_queries += 1
+                test_version.save()
+
+                payload = {
+                    "worker_version_id": str(test_version.id),
+                    "parents": [],
                 }
-            },
-            "parents": [],
-            "model_version": None,
-            "configuration": None,
-            "process": {
-                "id": str(self.process_2.id),
-                "activity_state": "disabled",
-                "corpus": str(self.corpus.id),
-                "chunks": 1,
-                "mode": "workers",
-                "name": None,
-                "state": "unscheduled",
-                "use_cache": False,
-            },
-            "summary": "Worker Recognizer @ main, develop, trunk, master, patate, pouet",
-            "use_gpu": False,
-        })
-        run = WorkerRun.objects.get(pk=pk)
-        # Check generated summary
-        self.assertEqual(run.summary, "Worker Recognizer @ main, develop, trunk, master, patate, pouet")
+                if model_version:
+                    payload["model_version_id"] = model_version.id
+                    # If there is a model version, it adds a query
+                    num_queries += 1
+                if config:
+                    payload["configuration_id"] = config.id
+                    # If there is a configuration, it adds a query
+                    num_queries += 1
+
+                with self.assertNumQueries(num_queries):
+                    response = self.client.post(
+                        reverse("api:worker-run-list", kwargs={"pk": str(self.process_2.id)}),
+                        data=payload,
+                        format="json",
+                    )
+                    if response.status_code == 400:
+                        self.assertDictEqual(response.json(), {})
+                    self.assertEqual(response.status_code, status.HTTP_201_CREATED)
+
+                created_run = WorkerRun.objects.get(pk=response.json()["id"])
+                self.assertEqual(created_run.summary, expected_summary)
 
     def test_retrieve_requires_login(self):
         with self.assertNumQueries(0):
@@ -657,7 +648,9 @@ class TestWorkerRuns(FixtureAPITestCase):
                 "gpu_usage": "disabled",
                 "model_usage": FeatureUsage.Disabled.value,
                 "revision": None,
+                "revision_url": None,
                 "version": 1,
+                "tag": None,
                 "created": self.version_1.created.isoformat().replace("+00:00", "Z"),
                 "state": "available",
                 "worker": {
@@ -713,7 +706,9 @@ class TestWorkerRuns(FixtureAPITestCase):
                 "gpu_usage": "disabled",
                 "model_usage": FeatureUsage.Disabled.value,
                 "revision": None,
+                "revision_url": None,
                 "version": 1,
+                "tag": None,
                 "created": self.version_1.created.isoformat().replace("+00:00", "Z"),
                 "state": "available",
                 "worker": {
@@ -798,7 +793,9 @@ class TestWorkerRuns(FixtureAPITestCase):
                 "gpu_usage": "disabled",
                 "model_usage": FeatureUsage.Disabled.value,
                 "revision": None,
+                "revision_url": None,
                 "version": 1,
+                "tag": None,
                 "created": self.version_1.created.isoformat().replace("+00:00", "Z"),
                 "state": "created",
                 "worker": {
@@ -913,7 +910,9 @@ class TestWorkerRuns(FixtureAPITestCase):
                 "gpu_usage": "disabled",
                 "model_usage": FeatureUsage.Disabled.value,
                 "revision": None,
+                "revision_url": None,
                 "version": 1,
+                "tag": None,
                 "created": self.version_1.created.isoformat().replace("+00:00", "Z"),
                 "state": "available",
                 "worker": {
@@ -980,7 +979,9 @@ class TestWorkerRuns(FixtureAPITestCase):
                 "gpu_usage": "disabled",
                 "model_usage": FeatureUsage.Disabled.value,
                 "revision": None,
+                "revision_url": None,
                 "version": 1,
+                "tag": None,
                 "created": self.version_1.created.isoformat().replace("+00:00", "Z"),
                 "state": "available",
                 "worker": {
@@ -1217,7 +1218,9 @@ class TestWorkerRuns(FixtureAPITestCase):
                 "gpu_usage": "disabled",
                 "model_usage": FeatureUsage.Disabled.value,
                 "revision": None,
+                "revision_url": None,
                 "version": 1,
+                "tag": None,
                 "created": self.version_1.created.isoformat().replace("+00:00", "Z"),
                 "state": "available",
                 "worker": {
@@ -1275,7 +1278,9 @@ class TestWorkerRuns(FixtureAPITestCase):
                 "gpu_usage": "disabled",
                 "model_usage": FeatureUsage.Disabled.value,
                 "revision": None,
+                "revision_url": None,
                 "version": 1,
+                "tag": None,
                 "created": self.version_1.created.isoformat().replace("+00:00", "Z"),
                 "state": "available",
                 "worker": {
@@ -1341,7 +1346,9 @@ class TestWorkerRuns(FixtureAPITestCase):
                 "gpu_usage": "disabled",
                 "model_usage": FeatureUsage.Disabled.value,
                 "revision": None,
+                "revision_url": None,
                 "version": 1,
+                "tag": None,
                 "created": self.version_1.created.isoformat().replace("+00:00", "Z"),
                 "state": "available",
                 "worker": {
@@ -1708,7 +1715,9 @@ class TestWorkerRuns(FixtureAPITestCase):
                         "gpu_usage": "disabled",
                         "model_usage": FeatureUsage.Supported.value,
                         "revision": None,
+                        "revision_url": None,
                         "version": version_with_model.version,
+                        "tag": None,
                         "created": version_with_model.created.isoformat().replace("+00:00", "Z"),
                         "state": "created",
                         "worker": {
@@ -1798,7 +1807,9 @@ class TestWorkerRuns(FixtureAPITestCase):
                 "gpu_usage": "disabled",
                 "model_usage": FeatureUsage.Required.value,
                 "revision": None,
+                "revision_url": None,
                 "version": version_with_model.version,
+                "tag": None,
                 "created": version_with_model.created.isoformat().replace("+00:00", "Z"),
                 "state": "created",
                 "worker": {
@@ -1864,7 +1875,9 @@ class TestWorkerRuns(FixtureAPITestCase):
                 "gpu_usage": "disabled",
                 "model_usage": FeatureUsage.Disabled.value,
                 "revision": None,
+                "revision_url": None,
                 "version": 1,
+                "tag": None,
                 "created": self.version_1.created.isoformat().replace("+00:00", "Z"),
                 "state": "available",
                 "worker": {
@@ -2082,7 +2095,9 @@ class TestWorkerRuns(FixtureAPITestCase):
                 "gpu_usage": "disabled",
                 "model_usage": FeatureUsage.Disabled.value,
                 "revision": None,
+                "revision_url": None,
                 "version": 1,
+                "tag": None,
                 "created": self.version_1.created.isoformat().replace("+00:00", "Z"),
                 "state": "available",
                 "worker": {
@@ -2140,7 +2155,9 @@ class TestWorkerRuns(FixtureAPITestCase):
                 "gpu_usage": "disabled",
                 "model_usage": FeatureUsage.Disabled.value,
                 "revision": None,
+                "revision_url": None,
                 "version": 1,
+                "tag": None,
                 "created": self.version_1.created.isoformat().replace("+00:00", "Z"),
                 "state": "available",
                 "worker": {
@@ -2203,7 +2220,9 @@ class TestWorkerRuns(FixtureAPITestCase):
                 "gpu_usage": "disabled",
                 "model_usage": FeatureUsage.Disabled.value,
                 "revision": None,
+                "revision_url": None,
                 "version": 1,
+                "tag": None,
                 "created": self.version_1.created.isoformat().replace("+00:00", "Z"),
                 "state": "available",
                 "worker": {
@@ -2557,7 +2576,9 @@ class TestWorkerRuns(FixtureAPITestCase):
                         "gpu_usage": "disabled",
                         "model_usage": FeatureUsage.Required.value,
                         "revision": None,
+                        "revision_url": None,
                         "version": version_with_model.version,
+                        "tag": None,
                         "created": version_with_model.created.isoformat().replace("+00:00", "Z"),
                         "state": "created",
                         "worker": {
@@ -2644,7 +2665,9 @@ class TestWorkerRuns(FixtureAPITestCase):
                 "gpu_usage": "disabled",
                 "model_usage": FeatureUsage.Required.value,
                 "revision": None,
+                "revision_url": None,
                 "version": version_with_model.version,
+                "tag": None,
                 "created": version_with_model.created.isoformat().replace("+00:00", "Z"),
                 "state": "created",
                 "worker": {
@@ -2709,7 +2732,9 @@ class TestWorkerRuns(FixtureAPITestCase):
                 "gpu_usage": "disabled",
                 "model_usage": FeatureUsage.Disabled.value,
                 "revision": None,
+                "revision_url": None,
                 "version": 1,
+                "tag": None,
                 "created": self.version_1.created.isoformat().replace("+00:00", "Z"),
                 "state": "available",
                 "worker": {
diff --git a/arkindex/process/tests/test_workerruns_use_gpu.py b/arkindex/process/tests/test_workerruns_use_gpu.py
index 5dbb690e864bc0dc98f67b79169abd98a9659da0..cc9e66b7de594fd578885b1d8eb37d4601f4c0a5 100644
--- a/arkindex/process/tests/test_workerruns_use_gpu.py
+++ b/arkindex/process/tests/test_workerruns_use_gpu.py
@@ -77,7 +77,9 @@ class TestWorkerRunsGPU(FixtureAPITestCase):
                         "gpu_usage": worker_version.gpu_usage.value,
                         "model_usage": FeatureUsage.Disabled.value,
                         "revision": None,
+                        "revision_url": None,
                         "version": worker_version.version,
+                        "tag": None,
                         "created": worker_version.created.isoformat().replace("+00:00", "Z"),
                         "state": "available",
                         "worker": {
@@ -129,7 +131,9 @@ class TestWorkerRunsGPU(FixtureAPITestCase):
                 "gpu_usage": FeatureUsage.Required.value,
                 "model_usage": FeatureUsage.Disabled.value,
                 "revision": None,
+                "revision_url": None,
                 "version": 2,
+                "tag": None,
                 "created": self.version_gpu_required.created.isoformat().replace("+00:00", "Z"),
                 "state": "available",
                 "worker": {
@@ -222,7 +226,9 @@ class TestWorkerRunsGPU(FixtureAPITestCase):
                         "gpu_usage": worker_version.gpu_usage.value,
                         "model_usage": FeatureUsage.Disabled.value,
                         "revision": None,
+                        "revision_url": None,
                         "version": worker_version.version,
+                        "tag": None,
                         "created": worker_version.created.isoformat().replace("+00:00", "Z"),
                         "state": "available",
                         "worker": {
diff --git a/arkindex/process/tests/test_workers.py b/arkindex/process/tests/test_workers.py
index a298519cefde678bf23b7bfc9980f1d5dee0cb11..d68b4d5939e7b3b9d48dcc9f87b6c929f80e4b55 100644
--- a/arkindex/process/tests/test_workers.py
+++ b/arkindex/process/tests/test_workers.py
@@ -12,7 +12,6 @@ from arkindex.process.models import (
     GitRefType,
     ProcessMode,
     Repository,
-    Revision,
     Worker,
     WorkerConfiguration,
     WorkerType,
@@ -93,6 +92,7 @@ class TestWorkersWorkerVersions(FixtureAPITestCase):
                 {
                     "id": str(self.worker_custom.id),
                     "repository_id": None,
+                    "repository_url": None,
                     "name": "Custom worker",
                     "description": "",
                     "slug": "custom",
@@ -102,6 +102,7 @@ class TestWorkersWorkerVersions(FixtureAPITestCase):
                 {
                     "id": str(self.worker_dla.id),
                     "repository_id": None,
+                    "repository_url": None,
                     "name": "Document layout analyser",
                     "description": "",
                     "slug": "dla",
@@ -111,6 +112,7 @@ class TestWorkersWorkerVersions(FixtureAPITestCase):
                 {
                     "id": str(self.init_worker.id),
                     "repository_id": None,
+                    "repository_url": None,
                     "name": "Elements Initialisation Worker",
                     "description": "",
                     "slug": "initialisation",
@@ -120,6 +122,7 @@ class TestWorkersWorkerVersions(FixtureAPITestCase):
                 {
                     "id": str(self.worker_file_import.id),
                     "repository_id": None,
+                    "repository_url": None,
                     "name": "File import",
                     "description": "",
                     "slug": "file_import",
@@ -129,6 +132,7 @@ class TestWorkersWorkerVersions(FixtureAPITestCase):
                 {
                     "id": str(self.worker_generic.id),
                     "repository_id": None,
+                    "repository_url": None,
                     "name": "Generic worker with a Model",
                     "description": "",
                     "slug": "generic",
@@ -138,6 +142,7 @@ class TestWorkersWorkerVersions(FixtureAPITestCase):
                 {
                     "id": str(self.worker_reco.id),
                     "repository_id": None,
+                    "repository_url": None,
                     "name": "Recognizer",
                     "description": "",
                     "slug": "reco",
@@ -147,6 +152,7 @@ class TestWorkersWorkerVersions(FixtureAPITestCase):
                 {
                     "id": str(self.worker_gpu.id),
                     "repository_id": None,
+                    "repository_url": None,
                     "name": "Worker requiring a GPU",
                     "description": "",
                     "slug": "worker-gpu",
@@ -180,6 +186,7 @@ class TestWorkersWorkerVersions(FixtureAPITestCase):
                 {
                     "id": str(self.worker_generic.id),
                     "repository_id": None,
+                    "repository_url": None,
                     "name": "Generic worker with a Model",
                     "description": "",
                     "slug": "generic",
@@ -209,6 +216,7 @@ class TestWorkersWorkerVersions(FixtureAPITestCase):
             "results": [{
                 "id": str(self.worker_reco.id),
                 "repository_id": None,
+                "repository_url": None,
                 "name": "Recognizer",
                 "description": "",
                 "slug": "reco",
@@ -280,6 +288,7 @@ class TestWorkersWorkerVersions(FixtureAPITestCase):
                 {
                     "id": str(self.worker_dla.id),
                     "repository_id": None,
+                    "repository_url": None,
                     "name": "Document layout analyser",
                     "description": "",
                     "slug": "dla",
@@ -307,6 +316,7 @@ class TestWorkersWorkerVersions(FixtureAPITestCase):
                 {
                     "id": str(self.worker_dla.id),
                     "repository_id": None,
+                    "repository_url": None,
                     "name": "Document layout analyser",
                     "description": "",
                     "slug": "dla",
@@ -335,6 +345,7 @@ class TestWorkersWorkerVersions(FixtureAPITestCase):
                 {
                     "id": str(self.worker_dla.id),
                     "repository_id": None,
+                    "repository_url": None,
                     "name": "Document layout analyser",
                     "description": "",
                     "slug": "dla",
@@ -375,6 +386,7 @@ class TestWorkersWorkerVersions(FixtureAPITestCase):
                 {
                     "id": str(self.worker_dla.id),
                     "repository_id": None,
+                    "repository_url": None,
                     "name": "Document layout analyser",
                     "description": "",
                     "slug": "dla",
@@ -426,6 +438,7 @@ class TestWorkersWorkerVersions(FixtureAPITestCase):
                 {
                     "id": str(self.worker_dla.id),
                     "repository_id": None,
+                    "repository_url": None,
                     "name": "Document layout analyser",
                     "description": "",
                     "slug": "dla",
@@ -461,6 +474,7 @@ class TestWorkersWorkerVersions(FixtureAPITestCase):
                 {
                     "id": str(worker_2.id),
                     "repository_id": str(repo2.id),
+                    "repository_url": None,
                     "name": "Worker 2",
                     "description": "",
                     "slug": "worker_2",
@@ -484,6 +498,7 @@ class TestWorkersWorkerVersions(FixtureAPITestCase):
         self.assertDictEqual(response.json(), {
             "id": str(self.worker_custom.id),
             "repository_id": None,
+            "repository_url": None,
             "name": "Custom worker",
             "description": "",
             "slug": "custom",
@@ -551,7 +566,8 @@ class TestWorkersWorkerVersions(FixtureAPITestCase):
                 data={
                     "name": "Worker post",
                     "slug": "worker_post",
-                    "type": self.worker_type_dla.slug
+                    "type": self.worker_type_dla.slug,
+                    "repository_url": "https://gitlab.com/NERV/eva/"
                 }
             )
             self.assertEqual(response.status_code, status.HTTP_201_CREATED)
@@ -568,6 +584,7 @@ class TestWorkersWorkerVersions(FixtureAPITestCase):
             "name": "Worker post",
             "description": "",
             "repository_id": None,
+            "repository_url": "https://gitlab.com/NERV/eva/",
             "slug": "worker_post",
             "type": "dla",
             "archived": False,
@@ -732,6 +749,7 @@ class TestWorkersWorkerVersions(FixtureAPITestCase):
                     "description": "New description",
                     "type": "dla",
                     "archived": False,
+                    "repository_url": "https://gitlab.com/NERV/eva"
                 },
             )
             self.assertEqual(response.status_code, status.HTTP_200_OK)
@@ -743,6 +761,7 @@ class TestWorkersWorkerVersions(FixtureAPITestCase):
             "description": "New description",
             "type": "dla",
             "repository_id": None,
+            "repository_url": "https://gitlab.com/NERV/eva",
             "archived": False,
         })
 
@@ -752,6 +771,7 @@ class TestWorkersWorkerVersions(FixtureAPITestCase):
         self.assertEqual(self.worker_reco.description, "New description")
         self.assertEqual(self.worker_reco.type, self.worker_type_dla)
         self.assertEqual(self.worker_reco.repository, None)
+        self.assertEqual(self.worker_reco.repository_url, "https://gitlab.com/NERV/eva")
         self.assertListEqual(filter_rights_mock.call_args_list, [
             call(self.user, Worker, Role.Contributor.value),
         ])
@@ -846,6 +866,7 @@ class TestWorkersWorkerVersions(FixtureAPITestCase):
                     "description": "New description",
                     "type": "dla",
                     "repository_id": None,
+                    "repository_url": None,
                     "archived": new_value,
                 })
 
@@ -925,6 +946,7 @@ class TestWorkersWorkerVersions(FixtureAPITestCase):
             "description": "New description",
             "type": "dla",
             "repository_id": None,
+            "repository_url": None,
             "archived": False,
         })
 
@@ -1017,6 +1039,7 @@ class TestWorkersWorkerVersions(FixtureAPITestCase):
                     "description": "",
                     "type": "recognizer",
                     "repository_id": None,
+                    "repository_url": None,
                     "archived": new_value,
                 })
 
@@ -1032,6 +1055,7 @@ class TestWorkersWorkerVersions(FixtureAPITestCase):
             version=2,
             configuration={"a": "bc"},
             state=WorkerVersionState.Error,
+            revision_url="https://gitlab.com/NERV/eva/commit/63e377e7f88c743d8428fc4e4eaedfc1c9356754"
         )
         last_version.created = "2050-09-09T09:09:09.090909Z"
         last_version.save()
@@ -1060,7 +1084,9 @@ class TestWorkersWorkerVersions(FixtureAPITestCase):
                         "slug": self.worker_reco.slug,
                     },
                     "version": 2,
+                    "tag": None,
                     "revision": None,
+                    "revision_url": "https://gitlab.com/NERV/eva/commit/63e377e7f88c743d8428fc4e4eaedfc1c9356754",
                     "created": "2050-09-09T09:09:09.090909Z",
                 },
                 {
@@ -1077,7 +1103,9 @@ class TestWorkersWorkerVersions(FixtureAPITestCase):
                         "slug": self.worker_reco.slug,
                     },
                     "version": 1,
+                    "tag": None,
                     "revision": None,
+                    "revision_url": None,
                     "created": self.version_1.created.isoformat().replace("+00:00", "Z"),
                 }
             ]
@@ -1248,30 +1276,11 @@ class TestWorkersWorkerVersions(FixtureAPITestCase):
             ]
         })
 
-    def test_create_version_return_existing_revision(self):
-        rev = self.repo.revisions.create(hash="001", message="something", author="someone")
-        self.version_1.revision_id = rev.id
-        self.version_1.version = None
-        self.version_1.save()
-        # A worker version already exists for this worker and this revision
-        with self.assertNumQueries(4):
-            response = self.client.post(
-                reverse("api:worker-versions", kwargs={"pk": str(self.worker_reco.id)}),
-                data={"revision_id": str(rev.id), "configuration": {"test": "test1"}},
-                format="json",
-                HTTP_AUTHORIZATION=f"Ponos {self.task.token}",
-            )
-            self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
-
-        self.assertDictEqual(response.json(), {
-            "revision_id": ["A version of this worker already exists with this revision"]
-        })
-
     def test_create_version_empty(self):
-        with self.assertNumQueries(2):
+        self.client.force_login(self.user)
+        with self.assertNumQueries(3):
             response = self.client.post(
-                reverse("api:worker-versions", kwargs={"pk": str(self.worker_reco.id)}),
-                HTTP_AUTHORIZATION=f"Ponos {self.task.token}",
+                reverse("api:worker-versions", kwargs={"pk": str(self.worker_reco.id)})
             )
             self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
 
@@ -1286,6 +1295,7 @@ class TestWorkersWorkerVersions(FixtureAPITestCase):
                 reverse("api:worker-versions", kwargs={"pk": str(self.worker_reco.id)}),
                 data={
                     "configuration": {},
+                    "revision_url": "https://gitlab.com/NERV/eva/commit/eva-01"
                 },
                 format="json",
                 HTTP_AUTHORIZATION=f"Ponos {self.task.token}",
@@ -1296,57 +1306,9 @@ class TestWorkersWorkerVersions(FixtureAPITestCase):
             "worker": ["This worker is archived."],
         })
 
-    def test_create_version_unrelated_revision(self):
-        """
-        It is not possible to create a version between a worker and a revision of two different repositories
-        """
-        self.worker_reco.repository = self.repo
+    def test_create_version_null_revision_url_requires_null_worker_repository_url(self):
+        self.worker_reco.repository_url = "https://gitlab.com/NERV/eva"
         self.worker_reco.save()
-        rev = Revision.objects.create(
-            hash="1337",
-            message="A worker import I am admin on",
-            author="Yann",
-            repo=Repository.objects.create(url="http://gitlab/repo")
-        )
-
-        with self.assertNumQueries(4):
-            response = self.client.post(
-                reverse("api:worker-versions", kwargs={"pk": str(self.worker_reco.id)}),
-                data={"revision_id": str(rev.id), "configuration": {"test": "test2"}},
-                format="json",
-                HTTP_AUTHORIZATION=f"Ponos {self.task.token}",
-            )
-            self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
-
-        self.assertDictEqual(response.json(), {
-            "revision_id": ["The revision must be part of the same repository as the worker."]
-        })
-
-    def test_create_version_revision_requires_worker_repository(self):
-        self.worker_custom.memberships.filter(user=self.user).update(level=Role.Admin.value)
-        self.client.force_login(self.user)
-        rev = self.repo.revisions.create(
-            hash="1337",
-            message="A worker import I am admin on",
-            author="Yann",
-        )
-
-        with self.assertNumQueries(5):
-            response = self.client.post(
-                reverse("api:worker-versions", kwargs={"pk": str(self.worker_custom.id)}),
-                data={"revision_id": str(rev.id), "configuration": {"test": "test2"}, "model_usage": FeatureUsage.Required.value},
-                format="json",
-            )
-            self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
-
-        self.assertDictEqual(response.json(), {
-            "revision_id": ["A revision cannot be set on a worker that is not linked to a repository."]
-        })
-
-    def test_create_version_null_revision_requires_null_worker_repository(self):
-        self.worker_reco.repository = self.repo
-        self.worker_reco.save()
-        self.worker_custom.memberships.filter(user=self.user).update(level=Role.Admin.value)
         self.client.force_login(self.user)
 
         with self.assertNumQueries(3):
@@ -1358,31 +1320,24 @@ class TestWorkersWorkerVersions(FixtureAPITestCase):
             self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
 
         self.assertDictEqual(response.json(), {
-            "revision_id": ["A revision must be set on a worker that is linked to a repository."],
+            "revision_url": ["A revision url is required when creating a version for a worker linked to a repository."],
         })
 
-    def test_create_version_null_revision_forbidden_task_auth(self):
+    def test_create_version_null_revision_url_forbidden_task_auth(self):
         """
         Ponos Task auth cannot create a version on a worker that is not linked to a repository.
         """
-        self.worker_custom.versions.create(version=41, configuration={})
-        rev = self.repo.revisions.create(
-            hash="1337",
-            message="A worker import I am admin on",
-            author="Yann",
-        )
-
-        with self.assertNumQueries(4):
+        with self.assertNumQueries(2):
             response = self.client.post(
                 reverse("api:worker-versions", kwargs={"pk": str(self.worker_custom.id)}),
-                data={"revision_id": str(rev.id), "configuration": {"test": "test2"}, "model_usage": FeatureUsage.Required.value},
+                data={"configuration": {"test": "test2"}, "model_usage": FeatureUsage.Required.value},
                 format="json",
                 HTTP_AUTHORIZATION=f"Ponos {self.task.token}",
             )
             self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
 
         self.assertDictEqual(response.json(), {
-            "revision_id": ["Task authentication requires to create a version on workers linked to a repository."]
+            "revision_url": ["This field is required when creating a version through Task authentication."]
         })
 
     @patch("arkindex.users.utils.get_max_level", return_value=Role.Contributor.value)
@@ -1402,30 +1357,44 @@ class TestWorkersWorkerVersions(FixtureAPITestCase):
         self.assertEqual(max_level_mock.call_count, 1)
         self.assertEqual(max_level_mock.call_args, call(self.user, self.worker_custom))
 
-    def test_create_version_user_auth_requires_null_repository(self):
+    def test_create_version_user_auth_with_worker_repository_url_ok(self):
         self.client.force_login(self.user)
-        self.worker_dla.repository = self.repo
+        self.worker_dla.repository_url = "https://gitlab.com/NERV/eva"
         self.worker_dla.save()
-        rev = self.repo.revisions.create(
-            hash="1337",
-            message="A worker import I am admin on",
-            author="Yann",
-        )
-        with self.assertNumQueries(5):
+
+        with self.assertNumQueries(7):
             response = self.client.post(
                 reverse("api:worker-versions", kwargs={"pk": str(self.worker_dla.id)}),
-                data={"revision_id": str(rev.id), "configuration": {"test": "test2"}, "model_usage": FeatureUsage.Required.value},
+                data={"revision_url": "https://gitlab.com/NERV/eva/commit/eva-01", "configuration": {"test": "test2"}, "model_usage": FeatureUsage.Required.value},
                 format="json",
             )
-            self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
+            self.assertEqual(response.status_code, status.HTTP_201_CREATED)
+
+        data = response.json()
+        new_version = self.worker_dla.versions.get(id=data["id"])
         self.assertDictEqual(response.json(), {
-            "revision_id": ["Ponos authentication is required to create a version on a worker linked to a repository."]
+            "id": str(new_version.id),
+            "configuration": {"test": "test2"},
+            "created": new_version.created.isoformat().replace("+00:00", "Z"),
+            "docker_image_iid": None,
+            "gpu_usage": FeatureUsage.Disabled.value,
+            "model_usage": FeatureUsage.Required.value,
+            "revision": None,
+            "revision_url": "https://gitlab.com/NERV/eva/commit/eva-01",
+            "state": WorkerVersionState.Created.value,
+            "version": 2,
+            "tag": None,
+            "worker": {
+                "id": str(self.worker_dla.id),
+                "name": "Document layout analyser",
+                "slug": "dla",
+                "type": "dla",
+            },
         })
 
-    def test_create_version_null_revision(self):
+    def test_create_version_null_revision_url(self):
         """
-        A worker version can be created with no revision on a worker that has no repository.
-        Its version number is automatically incremented.
+        A worker version can be created with no revision_url through user authentication
         """
         self.worker_custom.memberships.filter(user=self.user).update(level=Role.Admin.value)
         self.worker_custom.versions.create(version=41, configuration={})
@@ -1447,8 +1416,10 @@ class TestWorkersWorkerVersions(FixtureAPITestCase):
             "gpu_usage": FeatureUsage.Disabled.value,
             "model_usage": FeatureUsage.Required.value,
             "revision": None,
+            "revision_url": None,
             "state": WorkerVersionState.Created.value,
             "version": 42,
+            "tag": None,
             "worker": {
                 "id": str(self.worker_custom.id),
                 "name": "Custom worker",
@@ -1474,7 +1445,7 @@ class TestWorkersWorkerVersions(FixtureAPITestCase):
     def test_create_version(self):
         response = self.client.post(
             reverse("api:worker-versions", kwargs={"pk": str(self.worker_reco.id)}),
-            data={"configuration": {"test": "test2"}, "model_usage": FeatureUsage.Required.value},
+            data={"configuration": {"test": "test2"}, "model_usage": FeatureUsage.Required.value, "revision_url": "https://gitlab.com/NERV/eva/commit/eva-01"},
             format="json",
             HTTP_AUTHORIZATION=f"Ponos {self.task.token}",
         )
@@ -1488,10 +1459,54 @@ class TestWorkersWorkerVersions(FixtureAPITestCase):
         self.assertEqual(data["gpu_usage"], "disabled")
         self.assertEqual(data["model_usage"], FeatureUsage.Required.value)
 
+    def test_create_version_with_tag(self):
+        with self.assertNumQueries(7):
+            response = self.client.post(
+                reverse("api:worker-versions", kwargs={"pk": str(self.worker_reco.id)}),
+                data={"configuration": {"test": "test2"}, "model_usage": FeatureUsage.Required.value, "revision_url": "https://gitlab.com/NERV/eva/commit/eva-01", "tag": "eva-01"},
+                format="json",
+                HTTP_AUTHORIZATION=f"Ponos {self.task.token}",
+            )
+            self.assertEqual(response.status_code, status.HTTP_201_CREATED)
+
+        data = response.json()
+        self.assertNotEqual(data["id"], str(self.version_1.id))
+        self.assertEqual(data["configuration"], {"test": "test2"})
+        self.assertEqual(data["version"], 2)
+        self.assertEqual(data["state"], "created")
+        self.assertEqual(data["gpu_usage"], "disabled")
+        self.assertEqual(data["model_usage"], FeatureUsage.Required.value)
+        self.assertEqual(data["tag"], "eva-01")
+
+    def test_create_version_unique_tag(self):
+        self.version_1.tag = "eva-01"
+        self.version_1.save()
+
+        with self.assertNumQueries(3):
+            response = self.client.post(
+                reverse("api:worker-versions", kwargs={"pk": str(self.worker_reco.id)}),
+                data={"configuration": {"test": "test2"}, "revision_url": "https://gitlab.com/NERV/eva/commit/eva-01", "tag": "eva-01"},
+                format="json",
+                HTTP_AUTHORIZATION=f"Ponos {self.task.token}",
+            )
+            self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
+        self.assertDictEqual(response.json(), {"tag": ["A version already exists for this worker with this tag."]})
+
+    def test_create_version_empty_tag(self):
+        with self.assertNumQueries(2):
+            response = self.client.post(
+                reverse("api:worker-versions", kwargs={"pk": str(self.worker_reco.id)}),
+                data={"configuration": {"test": "test2"}, "revision_url": "https://gitlab.com/NERV/eva/commit/eva-01", "tag": ""},
+                format="json",
+                HTTP_AUTHORIZATION=f"Ponos {self.task.token}",
+            )
+            self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
+        self.assertDictEqual(response.json(), {"tag": ["This field may not be blank."]})
+
     def test_create_version_wrong_gpu_usage(self):
         response = self.client.post(
             reverse("api:worker-versions", kwargs={"pk": str(self.worker_reco.id)}),
-            data={"configuration": {"test": "test2"}, "gpu_usage": "not_supported"},
+            data={"configuration": {"test": "test2"}, "revision_url": "https://gitlab.com/NERV/eva/commit/eva-01", "gpu_usage": "not_supported"},
             format="json",
             HTTP_AUTHORIZATION=f"Ponos {self.task.token}",
         )
@@ -1502,6 +1517,7 @@ class TestWorkersWorkerVersions(FixtureAPITestCase):
             reverse("api:worker-versions", kwargs={"pk": str(self.worker_dla.id)}),
             data={
                 "configuration": {"beep": "boop"},
+                "revision_url": "https://gitlab.com/NERV/eva/commit/eva-01",
                 "gpu_usage": "disabled",
             },
             format="json",
@@ -1520,6 +1536,7 @@ class TestWorkersWorkerVersions(FixtureAPITestCase):
                     }
                 },
                 "gpu_usage": "disabled",
+                "revision_url": "https://gitlab.com/NERV/eva/commit/eva-01",
             },
             format="json",
             HTTP_AUTHORIZATION=f"Ponos {self.task.token}",
@@ -1551,6 +1568,7 @@ class TestWorkersWorkerVersions(FixtureAPITestCase):
                         "demo_dict": {"title": "Demo Dict", "type": "dict", "required": True, "default": {"a": "b", "c": "d"}},
                     }
                 },
+                "revision_url": "https://gitlab.com/NERV/eva/commit/eva-01",
                 "gpu_usage": "disabled",
             },
             format="json",
@@ -1593,6 +1611,7 @@ class TestWorkersWorkerVersions(FixtureAPITestCase):
                         "demo_choice": {"title": "Decisions", "type": "enum", "required": True, "default": 1, "choices": [1, 2, 3]}
                     }
                 },
+                "revision_url": "https://gitlab.com/NERV/eva/commit/eva-01",
                 "gpu_usage": "disabled",
             },
             format="json",
@@ -1621,6 +1640,7 @@ class TestWorkersWorkerVersions(FixtureAPITestCase):
                         "boolean_list": {"title": "It's a list of booleans", "type": "list", "required": False, "subtype": "bool", "default": [True, False, False]}
                     }
                 },
+                "revision_url": "https://gitlab.com/NERV/eva/commit/eva-01",
                 "gpu_usage": "disabled",
             },
             format="json",
@@ -1656,6 +1676,7 @@ class TestWorkersWorkerVersions(FixtureAPITestCase):
                         "other_model": {"title": "Model the second", "type": "model", "default": str(self.model.id)}
                     }
                 },
+                "revision_url": "https://gitlab.com/NERV/eva/commit/eva-01",
                 "gpu_usage": "disabled",
             },
             format="json",
@@ -2062,7 +2083,9 @@ class TestWorkersWorkerVersions(FixtureAPITestCase):
             "gpu_usage": "disabled",
             "model_usage": FeatureUsage.Disabled.value,
             "revision": None,
+            "revision_url": None,
             "version": 1,
+            "tag": None,
             "worker": {
                 "id": str(self.worker_reco.id),
                 "name": "Recognizer",
@@ -2085,7 +2108,9 @@ class TestWorkersWorkerVersions(FixtureAPITestCase):
             "gpu_usage": "disabled",
             "model_usage": FeatureUsage.Disabled.value,
             "revision": None,
+            "revision_url": None,
             "version": 1,
+            "tag": None,
             "worker": {
                 "id": str(self.worker_reco.id),
                 "name": "Recognizer",
@@ -2147,7 +2172,7 @@ class TestWorkersWorkerVersions(FixtureAPITestCase):
 
     def test_update_worker_version(self):
         """
-        Update worker version artifact, configuration and state
+        Update worker version artifact, configuration, state and tag
         """
         self.version_1.state = WorkerVersionState.Created
         self.version_1.save()
@@ -2158,6 +2183,7 @@ class TestWorkersWorkerVersions(FixtureAPITestCase):
                 "configuration": {"test": "test2"},
                 "docker_image_iid": "eva:unit-01",
                 "state": "error",
+                "tag": "eva-01"
             },
             format="json",
             HTTP_AUTHORIZATION=f"Ponos {self.task.token}",
@@ -2166,10 +2192,44 @@ class TestWorkersWorkerVersions(FixtureAPITestCase):
         data = response.json()
         self.assertEqual(data["id"], str(self.version_1.id))
         self.version_1.refresh_from_db()
-        self.assertEqual(data["configuration"], {"test": "test2"})
-        self.assertEqual(data["docker_image_iid"], "eva:unit-01")
-        self.assertEqual(data["state"], "error")
-        self.assertEqual(data["gpu_usage"], "disabled")
+        self.assertEqual(self.version_1.configuration, {"test": "test2"})
+        self.assertEqual(self.version_1.docker_image_iid, "eva:unit-01")
+        self.assertEqual(self.version_1.state, WorkerVersionState.Error)
+        self.assertEqual(self.version_1.gpu_usage, FeatureUsage.Disabled)
+        self.assertEqual(self.version_1.tag, "eva-01")
+
+    def test_update_worker_version_unique_tag(self):
+        self.worker_reco.versions.create(
+            docker_image_iid="registry.fake.com/workers/worker:12345",
+            state=WorkerVersionState.Available,
+            version=12,
+            tag="eva-01"
+        )
+
+        with self.assertNumQueries(3):
+            response = self.client.patch(
+                reverse("api:version-retrieve", kwargs={"pk": str(self.version_1.id)}),
+                data={
+                    "tag": "eva-01"
+                },
+                format="json",
+                HTTP_AUTHORIZATION=f"Ponos {self.task.token}",
+            )
+            self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
+        self.assertDictEqual(response.json(), {"tag": ["A version already exists for this worker with this tag."]})
+
+    def test_update_worker_version_empty_tag(self):
+        with self.assertNumQueries(2):
+            response = self.client.patch(
+                reverse("api:version-retrieve", kwargs={"pk": str(self.version_1.id)}),
+                data={
+                    "tag": ""
+                },
+                format="json",
+                HTTP_AUTHORIZATION=f"Ponos {self.task.token}",
+            )
+            self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
+        self.assertDictEqual(response.json(), {"tag": ["This field may not be blank."]})
 
     def test_cannot_update_worker_version_revision_ignored(self):
         rev = self.repo.revisions.create(hash="001", message="something", author="someone")
@@ -2192,36 +2252,24 @@ class TestWorkersWorkerVersions(FixtureAPITestCase):
         data = response.json()
         self.assertEqual(data["id"], str(self.version_1.id))
         self.version_1.refresh_from_db()
-        self.assertEqual(data["revision"]["id"], str(rev.id))
+        self.assertEqual(self.version_1.revision.id, rev.id)
 
-    def test_update_version_valid(self):
-        self.version_1.state = WorkerVersionState.Created
-        self.version_1.save()
-
-        process = self.corpus.processes.create(
-            creator=self.user,
-            mode=ProcessMode.Workers,
-            farm=self.farm,
-        )
-        process.run()
-
-        response = self.client.patch(
-            reverse("api:version-retrieve", kwargs={"pk": str(self.version_1.id)}),
-            data={
-                "configuration": {"test": "test2"},
-                "docker_image_iid": "registry.nerv.co.jp/cruel/angel/thesis:latest",
-                "state": "available",
-            },
-            format="json",
-            HTTP_AUTHORIZATION=f"Ponos {self.task.token}",
-        )
-        self.assertEqual(response.status_code, status.HTTP_200_OK)
+    def test_cannot_update_worker_version_revision_url_ignored(self):
+        with self.assertNumQueries(4):
+            response = self.client.patch(
+                reverse("api:version-retrieve", kwargs={"pk": str(self.version_1.id)}),
+                data={
+                    "revision_url": "https://gitlab.com/NERV/eva/commit/eva-01"
+                },
+                format="json",
+                HTTP_AUTHORIZATION=f"Ponos {self.task.token}",
+            )
+            # revision_url just gets ignored
+            self.assertEqual(response.status_code, status.HTTP_200_OK)
         data = response.json()
         self.assertEqual(data["id"], str(self.version_1.id))
-        self.assertEqual(data["configuration"], {"test": "test2"})
-        self.assertEqual(data["docker_image_iid"], "registry.nerv.co.jp/cruel/angel/thesis:latest")
-        self.assertEqual(data["state"], "available")
-        self.assertEqual(data["gpu_usage"], "disabled")
+        self.version_1.refresh_from_db()
+        self.assertEqual(self.version_1.revision_url, None)
 
     def test_update_version_available_requires_docker_image(self):
         self.version_1.state = WorkerVersionState.Created
@@ -2286,7 +2334,9 @@ class TestWorkersWorkerVersions(FixtureAPITestCase):
                     "id": str(self.version_2.id),
                     "configuration": {"test": 42},
                     "revision": None,
+                    "revision_url": None,
                     "version": 1,
+                    "tag": None,
                     "gpu_usage": "disabled",
                     "model_usage": FeatureUsage.Disabled.value,
                     "docker_image_iid": self.version_2.docker_image_iid,
@@ -2307,7 +2357,9 @@ class TestWorkersWorkerVersions(FixtureAPITestCase):
                     "id": str(self.version_1.id),
                     "configuration": {"test": 42},
                     "revision": None,
+                    "revision_url": None,
                     "version": 1,
+                    "tag": None,
                     "gpu_usage": "disabled",
                     "model_usage": FeatureUsage.Disabled.value,
                     "docker_image_iid": self.version_1.docker_image_iid,
@@ -2360,7 +2412,9 @@ class TestWorkersWorkerVersions(FixtureAPITestCase):
                     "id": str(self.version_2.id),
                     "configuration": {"test": 42},
                     "revision": None,
+                    "revision_url": None,
                     "version": 1,
+                    "tag": None,
                     "gpu_usage": "disabled",
                     "model_usage": FeatureUsage.Disabled.value,
                     "docker_image_iid": self.version_2.docker_image_iid,
@@ -2381,7 +2435,9 @@ class TestWorkersWorkerVersions(FixtureAPITestCase):
                     "id": str(self.version_1.id),
                     "configuration": {"test": 42},
                     "revision": None,
+                    "revision_url": None,
                     "version": 1,
+                    "tag": None,
                     "gpu_usage": "disabled",
                     "model_usage": FeatureUsage.Disabled.value,
                     "docker_image_iid": self.version_2.docker_image_iid,
@@ -2430,7 +2486,9 @@ class TestWorkersWorkerVersions(FixtureAPITestCase):
                     "id": str(self.version_2.id),
                     "configuration": {"test": 42},
                     "revision": None,
+                    "revision_url": None,
                     "version": 1,
+                    "tag": None,
                     "gpu_usage": "disabled",
                     "model_usage": FeatureUsage.Disabled.value,
                     "docker_image_iid": self.version_2.docker_image_iid,
@@ -2466,7 +2524,9 @@ class TestWorkersWorkerVersions(FixtureAPITestCase):
                     "id": str(self.version_1.id),
                     "configuration": {"test": 42},
                     "revision": None,
+                    "revision_url": None,
                     "version": 1,
+                    "tag": None,
                     "gpu_usage": "disabled",
                     "model_usage": FeatureUsage.Disabled.value,
                     "docker_image_iid": self.version_1.docker_image_iid,
@@ -2521,7 +2581,9 @@ class TestWorkersWorkerVersions(FixtureAPITestCase):
                     "id": str(self.version_1.id),
                     "configuration": {"test": 42},
                     "revision": None,
+                    "revision_url": None,
                     "version": 1,
+                    "tag": None,
                     "gpu_usage": "disabled",
                     "model_usage": FeatureUsage.Disabled.value,
                     "docker_image_iid": self.version_1.docker_image_iid,
diff --git a/arkindex/sql_validation/indexer_prefetch.sql b/arkindex/sql_validation/indexer_prefetch.sql
index 6121dbbecc6ec5cef02ed9e17a078e6be4b0eb08..8a5be55e93357c575a67a61f46c96abd5991d32e 100644
--- a/arkindex/sql_validation/indexer_prefetch.sql
+++ b/arkindex/sql_validation/indexer_prefetch.sql
@@ -69,6 +69,8 @@ SELECT "process_workerversion"."id",
        "process_workerversion"."gpu_usage",
        "process_workerversion"."model_usage",
        "process_workerversion"."docker_image_iid",
+       "process_workerversion"."revision_url",
+       "process_workerversion"."tag",
        "process_workerversion"."created",
        "process_workerversion"."updated"
 FROM "process_workerversion"
@@ -81,7 +83,8 @@ SELECT "process_worker"."id",
        "process_worker"."description",
        "process_worker"."repository_id",
        "process_worker"."public",
-       "process_worker"."archived"
+       "process_worker"."archived",
+       "process_worker"."repository_url"
 FROM "process_worker"
 WHERE "process_worker"."id" IN ('{worker_id}'::uuid);
 
@@ -158,6 +161,8 @@ SELECT "process_workerversion"."id",
        "process_workerversion"."gpu_usage",
        "process_workerversion"."model_usage",
        "process_workerversion"."docker_image_iid",
+       "process_workerversion"."revision_url",
+       "process_workerversion"."tag",
        "process_workerversion"."created",
        "process_workerversion"."updated"
 FROM "process_workerversion"
@@ -170,7 +175,8 @@ SELECT "process_worker"."id",
        "process_worker"."description",
        "process_worker"."repository_id",
        "process_worker"."public",
-       "process_worker"."archived"
+       "process_worker"."archived",
+       "process_worker"."repository_url"
 FROM "process_worker"
 WHERE "process_worker"."id" IN ('{worker_id}'::uuid);