diff --git a/build/fixtures/test_models.json b/build/fixtures/test_models.json
index 6989732..afe52e2 100644
--- a/build/fixtures/test_models.json
+++ b/build/fixtures/test_models.json
@@ -23,8 +23,8 @@
     "branch": "mr8.1",
     "release": "release-mr8.1",
     "distribution": "buster",
-    "projects": "kamailio,lua-ngcp-kamailio,ngcp-panel",
-    "built_projects": "kamailio,lua-ngcp-kamailio"
+    "projects": "asterisk-voicemail,lua-ngcp-kamailio,ngcp-panel",
+    "built_projects": null
   }
 }
 ]
diff --git a/build/models/__init__.py b/build/models/__init__.py
index 474687c..8c7afd1 100644
--- a/build/models/__init__.py
+++ b/build/models/__init__.py
@@ -19,7 +19,7 @@ from django.db.models import signals
 
 from .br import BuildRelease
 from build.tasks import build_release
-from build.utils import trigger_build
+from build.tasks import build_resume
 from repoapi.models import JenkinsBuildInfo
 
 logger = logging.getLogger(__name__)
@@ -66,31 +66,7 @@ def jbi_manage(sender, **kwargs):
         logger.debug("BuildRelease:%s jbi:%s skip", br, jbi)
         return
     br.remove_triggered(jbi)
-    params = {
-        "release_uuid": br.uuid,
-        "trigger_release": br.release,
-        "trigger_branch_or_tag": br.branch_or_tag,
-        "trigger_distribution": br.distribution,
-    }
-    size = settings.BUILD_POOL - br.pool_size
-    if size <= 0:
-        logger.info(
-            "BuildRelease:%s No more room for new builds,"
-            " wait for next slot",
-            br,
-        )
-    for step in range(size):
-        prj = br.next
-        if prj:
-            params["project"] = "{}-get-code".format(prj)
-            logger.debug(
-                "trigger:%s for BuildRelease:%s", params["project"], br
-            )
-            trigger_build(**params)
-            br.append_triggered(prj)
-        else:
-            logger.debug("BuildRelease:%s has no next", br)
-            break
+    build_resume.delay(br.id)
 
 
 post_save = signals.post_save.connect
diff --git a/build/models/br.py b/build/models/br.py
index d3b5182..d72fca4 100644
--- a/build/models/br.py
+++ b/build/models/br.py
@@ -96,6 +96,7 @@ class BuildRelease(models.Model):
     failed_projects = models.TextField(null=True, editable=False)
     pool_size = models.SmallIntegerField(default=0, editable=False)
     objects = BuildReleaseManager()
+    release_jobs_len = len(",".join(settings.RELEASE_JOBS))
 
     def __str__(self):
         return "%s[%s]" % (self.release, self.uuid)
@@ -104,6 +105,17 @@ class BuildRelease(models.Model):
         self.projects = ",".join(self.config.projects)
         self.save()
 
+    def resume(self):
+        if not self.done:
+            from build.tasks import build_resume
+
+            build_resume.delay(self.id)
+
+    @property
+    def done(self):
+        built_len = len(self.built_projects)
+        return built_len == self.release_jobs_len + 1 + len(self.projects)
+
     @property
     def projects_list(self):
         return [x.strip() for x in self.projects.split(",")]
@@ -207,9 +219,7 @@ class BuildRelease(models.Model):
     def _next(self):
         if self.built_projects is None:
             return self.build_deps[0][0]
-        built_len = len(self.built_projects)
-        release_jobs_len = len(",".join(settings.RELEASE_JOBS))
-        if built_len == release_jobs_len + 1 + len(self.projects):
+        if self.done:
             return
         t_list = self.triggered_projects_list
         built_list = self.built_projects_list
diff --git a/build/tasks.py b/build/tasks.py
index 8101ff3..aba2b70 100644
--- a/build/tasks.py
+++ b/build/tasks.py
@@ -15,6 +15,7 @@
 import logging
 
 from celery import shared_task
+from django.conf import settings
 
 from build.models.br import BuildRelease
 from build.utils import trigger_build
@@ -55,3 +56,37 @@ def build_project(pk, project):
     )
     br.pool_size += 1
     logger.info("%s triggered" % url)
+
+
+@shared_task(ignore_result=True)
+def build_resume(pk):
+    try:
+        br = BuildRelease.objects.get(id=pk)
+    except BuildRelease.DoesNotExist:
+        logger.error("can't resume on unknown release with id:%s", pk)
+        return
+    params = {
+        "release_uuid": br.uuid,
+        "trigger_release": br.release,
+        "trigger_branch_or_tag": br.branch_or_tag,
+        "trigger_distribution": br.distribution,
+    }
+    size = settings.BUILD_POOL - br.pool_size
+    if size <= 0:
+        logger.info(
+            "BuildRelease:%s No more room for new builds,"
+            " wait for next slot",
+            br,
+        )
+    for step in range(size):
+        prj = br.next
+        if prj:
+            params["project"] = "{}-get-code".format(prj)
+            logger.debug(
+                "trigger:%s for BuildRelease:%s", params["project"], br
+            )
+            trigger_build(**params)
+            br.append_triggered(prj)
+        else:
+            logger.debug("BuildRelease:%s has no next", br)
+            break
diff --git a/build/test/test_models.py b/build/test/test_models.py
index 6d860e1..8f5a274 100644
--- a/build/test/test_models.py
+++ b/build/test/test_models.py
@@ -12,7 +12,6 @@
 #
 # You should have received a copy of the GNU General Public License along
 # with this program.  If not, see <http://www.gnu.org/licenses/>.
-from unittest.mock import call
 from unittest.mock import MagicMock
 from unittest.mock import patch
 
@@ -20,7 +19,6 @@ from django.test import override_settings
 from django.test import TestCase
 
 from build.models import BuildRelease
-from build.models import jbi_manage
 from repoapi.models import JenkinsBuildInfo
 from repoapi.test.base import BaseTest
 
@@ -140,8 +138,14 @@ class BuildReleaseTestCase(TestCase):
 
 @override_settings(DEBUG=True)
 class BuildReleaseStepsTest(TestCase):
+    fixtures = [
+        "test_models",
+    ]
+    release = "release-mr8.1"
+    release_uuid = "dbe569f7-eab6-4532-a6d1-d31fb559648b"
+
     def setUp(self):
-        self.br = BuildRelease.objects.create_build_release("AAA", "trunk")
+        self.br = BuildRelease.objects.get(uuid=self.release_uuid)
         self.br.pool_size = 1
         self.jbi = MagicMock()
         self.jbi.result = "SUCCESS"
@@ -296,17 +300,18 @@ class BuildReleaseStepsTest(TestCase):
         self.assertIsNone(self.br.next)
 
 
-@override_settings(
-    DEBUG=True,
-    JBI_ALLOWED_HOSTS=["fake.local"],
-    CELERY_EAGER_PROPAGATES_EXCEPTIONS=True,
-)
+@override_settings(DEBUG=True, JBI_ALLOWED_HOSTS=["fake.local"])
 @patch("repoapi.utils.dlfile")
+@patch("build.models.build_resume")
 class JBIManageTest(TestCase):
-    @patch("build.models.trigger_build")
-    def test_jbi_manage_ko(self, tb, dl):
-        BuildRelease.objects.create_build_release("AAA", "trunk")
-        jbi = JenkinsBuildInfo.objects.create(
+    fixtures = [
+        "test_models",
+    ]
+    release = "release-mr8.1"
+    release_uuid = "dbe569f7-eab6-4532-a6d1-d31fb559648b"
+
+    def test_jbi_manage_ko(self, build_resume, dl):
+        JenkinsBuildInfo.objects.create(
             job_url="http://fake.local/job/release-copy-debs-yml/",
             projectname="release-copy-debs-yml",
             jobname="release-copy-debs-yml",
@@ -316,173 +321,82 @@ class JBIManageTest(TestCase):
             buildnumber=1,
             result="SUCCESS",
         )
-        params = {"instance": jbi, "created": True}
-        jbi_manage(JenkinsBuildInfo, **params)
-        tb.assert_not_called()
+        build_resume.delay.assert_not_called()
 
-    @patch("build.models.trigger_build")
-    def test_jbi_manage_ok_release_job(self, tb, dl):
-        br = BuildRelease.objects.create_build_release("UUID_mr8.1", "mr8.1")
+    def test_jbi_manage_ko_url(self, build_resume, dl):
+        JenkinsBuildInfo.objects.create(
+            job_url="http://other.local/job/release-copy-debs-yml/",
+            projectname="release-copy-debs-yml",
+            jobname="release-copy-debs-yml",
+            param_tag="UUIDA",
+            param_release=self.release,
+            param_release_uuid=self.release_uuid,
+            buildnumber=1,
+            result="SUCCESS",
+        )
+        build_resume.delay.assert_not_called()
+
+    def test_jbi_manage_ok_release_job(self, build_resume, dl):
+        br = BuildRelease.objects.get(uuid=self.release_uuid)
         self.assertEqual(br.pool_size, 0)
         JenkinsBuildInfo.objects.create(
             job_url="http://fake.local/job/release-copy-debs-yml/",
             projectname="release-copy-debs-yml",
             jobname="release-copy-debs-yml",
             tag="UUIDA",
-            param_release="mr8.1",
-            param_release_uuid="UUID_mr8.1",
+            param_release=self.release,
+            param_release_uuid=self.release_uuid,
             buildnumber=1,
             result="SUCCESS",
         )
         br = BuildRelease.objects.get(pk=br.pk)
         self.assertEqual(br.built_projects, "release-copy-debs-yml")
-        params = {
-            "project": "data-hal-get-code",
-            "release_uuid": br.uuid,
-            "trigger_release": br.release,
-            "trigger_branch_or_tag": br.branch_or_tag,
-            "trigger_distribution": br.distribution,
-        }
-        tb.assert_called_once_with(**params)
-        self.assertEqual(br.pool_size, 1)
-        self.assertEqual(br.triggered_projects, "data-hal")
+        build_resume.delay.assert_called_once_with(br.pk)
 
-    @patch("build.models.trigger_build")
-    def test_jbi_manage_skip(self, tb, dl):
-        br = BuildRelease.objects.create_build_release("UUID_mr8.1", "mr8.1")
+    def test_jbi_manage_skip(self, build_resume, dl):
+        br = BuildRelease.objects.get(uuid=self.release_uuid)
         br.pool_size = 1
         br.triggered_projects = "kamailio"
         br.save()
-        jbi = JenkinsBuildInfo.objects.create(
-            job_url="http://fake.local/job/kamailio-get-code/",
+        JenkinsBuildInfo.objects.create(
+            job_url="http://fake.local/job/kamailio-binaries/",
             projectname="kamailio",
-            jobname="kamailio-get-code",
+            jobname="kamailio-binaries",
             tag="UUIDA",
-            param_release="mr8.1",
-            param_release_uuid="UUID_mr8.1",
+            param_release=self.release,
+            param_release_uuid=self.release_uuid,
             buildnumber=1,
             result="SUCCESS",
         )
         br = BuildRelease.objects.get(pk=br.pk)
         self.assertIsNone(br.built_projects)
-        params = {"instance": jbi, "created": True}
-        jbi_manage(JenkinsBuildInfo, **params)
-        tb.assert_not_called()
+        build_resume.delay.assert_not_called()
         self.assertEqual(br.pool_size, 1)
         self.assertEqual(br.triggered_projects, "kamailio")
 
-    @override_settings(BUILD_POOL=2)
-    @patch("build.models.trigger_build")
-    def test_jbi_manage_pool(self, tb, dl):
-        br = BuildRelease.objects.create_build_release("UUID_mr8.1", "mr8.1")
-        self.assertEqual(br.pool_size, 0)
-        JenkinsBuildInfo.objects.create(
-            job_url="http://fake.local/job/release-copy-debs-yml/",
-            projectname="release-copy-debs-yml",
-            jobname="release-copy-debs-yml",
-            tag="UUIDA",
-            param_release="mr8.1",
-            param_release_uuid="UUID_mr8.1",
-            buildnumber=1,
-            result="SUCCESS",
-        )
-        br = BuildRelease.objects.get(pk=br.pk)
-        self.assertEqual(br.built_projects, "release-copy-debs-yml")
-        params = {
-            "project": "data-hal-get-code",
-            "release_uuid": br.uuid,
-            "trigger_release": br.release,
-            "trigger_branch_or_tag": br.branch_or_tag,
-            "trigger_distribution": br.distribution,
-        }
-        calls = [call(**params)]
-        params["project"] = "libinewrate-get-code"
-        calls.append(call(**params))
-        tb.assert_has_calls(calls)
-        self.assertEqual(br.pool_size, 2)
-        self.assertEqual(br.triggered_projects, "data-hal,libinewrate")
-
-    @override_settings(BUILD_POOL=2)
-    @patch("build.models.trigger_build")
-    def test_jbi_manage_pool_building(self, tb, dl):
-        self.test_jbi_manage_pool()
-        br = BuildRelease.objects.first()
-        self.assertEqual(br.pool_size, 2)
-        JenkinsBuildInfo.objects.create(
-            job_url="http://fake.local/job/data-hal-binaries/",
-            projectname="data-hal",
-            jobname="data-hal-binaries",
-            tag="UUIDA",
-            param_release="release-mr8.1",
-            param_release_uuid="UUID_mr8.1",
-            buildnumber=1,
-            result="SUCCESS",
-        )
-        br = BuildRelease.objects.first()
-        self.assertEqual(br.pool_size, 2)
-        self.assertEqual(br.triggered_projects, "data-hal,libinewrate")
-        JenkinsBuildInfo.objects.create(
-            job_url="http://fake.local/job/libinewrate-binaries/",
-            projectname="libinewrate",
-            jobname="libinewrate-binaries",
-            tag="UUIDA",
-            param_release="release-mr8.1",
-            param_release_uuid="UUID_mr8.1",
-            buildnumber=1,
-            result="SUCCESS",
-        )
-        br = BuildRelease.objects.first()
-        self.assertEqual(br.pool_size, 2)
-        self.assertEqual(br.triggered_projects, "data-hal,libinewrate")
-
-    @override_settings(BUILD_POOL=2)
-    @patch("build.models.trigger_build")
-    def test_jbi_manage_pool_next(self, tb, dl):
-        self.test_jbi_manage_pool()
-        br = BuildRelease.objects.first()
-        self.assertEqual(br.pool_size, 2)
-        JenkinsBuildInfo.objects.create(
-            job_url="http://fake.local/job/data-hal-repos/",
-            projectname="data-hal",
-            jobname="data-hal-repos",
-            tag="UUIDA",
-            param_release="release-mr8.1",
-            param_release_uuid="UUID_mr8.1",
-            buildnumber=1,
-            result="SUCCESS",
-        )
-        br = BuildRelease.objects.get(pk=br.pk)
-        self.assertEqual(br.built_projects, "release-copy-debs-yml,data-hal")
-        params = {
-            "project": "libswrate-get-code",
-            "release_uuid": br.uuid,
-            "trigger_release": br.release,
-            "trigger_branch_or_tag": br.branch_or_tag,
-            "trigger_distribution": br.distribution,
-        }
-        tb.assert_called_once_with(**params)
-        self.assertEqual(br.pool_size, 2)
-        self.assertEqual(br.triggered_projects, "libinewrate,libswrate")
-
-
-@override_settings(
-    DEBUG=True, CELERY_EAGER_PROPAGATES_EXCEPTIONS=True,
-)
+
+@override_settings(DEBUG=True)
 class BRManageTest(TestCase):
     @patch("build.tasks.trigger_copy_deps")
-    @patch("build.models.trigger_build")
-    def test_br_manage(self, tb, rb):
+    @patch("build.models.build_resume")
+    def test_br_manage(self, build_resume, trigger_copy_deps):
         br = BuildRelease.objects.create_build_release("UUID_mr8.1", "mr8.1")
-        tb.assert_not_called()
-        rb.assert_called_once_with(
+        build_resume.delay.assert_not_called()
+        trigger_copy_deps.assert_called_once_with(
             internal=True, release=br.release, release_uuid=br.uuid
         )
 
 
 @override_settings(DEBUG=True)
 class BuildReleaseRetriggerTest(TestCase):
+    fixtures = [
+        "test_models",
+    ]
+    release = "release-mr8.1"
+    release_uuid = "dbe569f7-eab6-4532-a6d1-d31fb559648b"
+
     def setUp(self):
-        self.br = BuildRelease.objects.create_build_release("AAA", "trunk")
+        self.br = BuildRelease.objects.get(uuid=self.release_uuid)
         self.jbi = MagicMock()
         self.jbi.result = "SUCCESS"
 
diff --git a/build/test/test_rest.py b/build/test/test_rest.py
index 943bab6..57186ce 100644
--- a/build/test/test_rest.py
+++ b/build/test/test_rest.py
@@ -35,7 +35,7 @@ class APIAuthenticatedTestCase(BaseTest, APITestCase):
         self.client.credentials(HTTP_API_KEY=self.app_key.key)
 
 
-@override_settings(DEBUG=True)
+@override_settings(DEBUG=True, JBI_ALLOWED_HOSTS=["fake.local"])
 class TestRest(APIAuthenticatedTestCase):
     def setUp(self):
         super(TestRest, self).setUp()
@@ -103,7 +103,7 @@ class TestRest(APIAuthenticatedTestCase):
         self.assertEqual(len(projects), 75)
 
 
-@override_settings(DEBUG=True)
+@override_settings(DEBUG=True, JBI_ALLOWED_HOSTS=["fake.local"])
 class TestBuildRest(APIAuthenticatedTestCase):
     fixtures = [
         "test_models",
@@ -120,7 +120,7 @@ class TestBuildRest(APIAuthenticatedTestCase):
         self.assertEqual(response.status_code, status.HTTP_201_CREATED)
 
 
-@override_settings(DEBUG=True)
+@override_settings(DEBUG=True, JBI_ALLOWED_HOSTS=["fake.local"])
 class TestBuildDeleteRest(APIAuthenticatedTestCase):
     fixtures = [
         "test_models",
@@ -184,7 +184,7 @@ class TestBuildDeleteRest(APIAuthenticatedTestCase):
         )
 
 
-@override_settings(DEBUG=True)
+@override_settings(DEBUG=True, JBI_ALLOWED_HOSTS=["fake.local"])
 class TestBuildPatchRest(APIAuthenticatedTestCase):
     fixtures = [
         "test_models",
diff --git a/build/test/test_tasks.py b/build/test/test_tasks.py
new file mode 100644
index 0000000..4c93c62
--- /dev/null
+++ b/build/test/test_tasks.py
@@ -0,0 +1,149 @@
+# Copyright (C) 2017 The Sipwise Team - http://sipwise.com
+#
+# This program is free software: you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the Free
+# Software Foundation, either version 3 of the License, or (at your option)
+# any later version.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+# more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program.  If not, see <http://www.gnu.org/licenses/>.
+from unittest.mock import call
+from unittest.mock import patch
+
+from django.test import override_settings
+from django.test import TestCase
+
+from build.models import BuildRelease
+from repoapi.models import JenkinsBuildInfo
+
+
+@override_settings(DEBUG=True, JBI_ALLOWED_HOSTS=["fake.local"])
+@patch("repoapi.utils.dlfile")
+@patch("build.tasks.trigger_build")
+class JBIManageTest(TestCase):
+    fixtures = [
+        "test_models",
+    ]
+    release = "release-mr8.1"
+    release_uuid = "dbe569f7-eab6-4532-a6d1-d31fb559648b"
+
+    def test_jbi_manage_ok_release_job(self, tb, dl):
+        br = BuildRelease.objects.get(uuid=self.release_uuid)
+        self.assertEqual(br.pool_size, 0)
+        JenkinsBuildInfo.objects.create(
+            job_url="http://fake.local/job/release-copy-debs-yml/",
+            projectname="release-copy-debs-yml",
+            jobname="release-copy-debs-yml",
+            tag="UUIDA",
+            param_release=self.release,
+            param_release_uuid=self.release_uuid,
+            buildnumber=1,
+            result="SUCCESS",
+        )
+        params = {
+            "project": "data-hal-get-code",
+            "release_uuid": br.uuid,
+            "trigger_release": br.release,
+            "trigger_branch_or_tag": br.branch_or_tag,
+            "trigger_distribution": br.distribution,
+        }
+        tb.assert_called_once_with(**params)
+        br = BuildRelease.objects.get(uuid=self.release_uuid)
+        self.assertEqual(br.pool_size, 1)
+        self.assertEqual(br.triggered_projects, "data-hal")
+
+    @override_settings(BUILD_POOL=2)
+    def test_jbi_manage_pool(self, tb, dl):
+        br = BuildRelease.objects.get(uuid=self.release_uuid)
+        self.assertEqual(br.pool_size, 0)
+        JenkinsBuildInfo.objects.create(
+            job_url="http://fake.local/job/release-copy-debs-yml/",
+            projectname="release-copy-debs-yml",
+            jobname="release-copy-debs-yml",
+            tag="UUIDA",
+            param_release="mr8.1",
+            param_release_uuid=self.release_uuid,
+            buildnumber=1,
+            result="SUCCESS",
+        )
+        br = BuildRelease.objects.get(id=br.pk)
+        self.assertEqual(br.built_projects, "release-copy-debs-yml")
+        params = {
+            "project": "data-hal-get-code",
+            "release_uuid": br.uuid,
+            "trigger_release": br.release,
+            "trigger_branch_or_tag": br.branch_or_tag,
+            "trigger_distribution": br.distribution,
+        }
+        calls = [call(**params)]
+        params["project"] = "libinewrate-get-code"
+        calls.append(call(**params))
+        tb.assert_has_calls(calls)
+        br = BuildRelease.objects.get(pk=br.pk)
+        self.assertEqual(br.pool_size, 2)
+        self.assertEqual(br.triggered_projects, "data-hal,libinewrate")
+
+    @override_settings(BUILD_POOL=2)
+    def test_jbi_manage_pool_building(self, tb, dl):
+        self.test_jbi_manage_pool()
+        br = BuildRelease.objects.get(uuid=self.release_uuid)
+        self.assertEqual(br.pool_size, 2)
+        JenkinsBuildInfo.objects.create(
+            job_url="http://fake.local/job/data-hal-binaries/",
+            projectname="data-hal",
+            jobname="data-hal-binaries",
+            tag="UUIDA",
+            param_release=self.release,
+            param_release_uuid=self.release_uuid,
+            buildnumber=1,
+            result="SUCCESS",
+        )
+        br = BuildRelease.objects.get(id=br.pk)
+        self.assertEqual(br.pool_size, 2)
+        self.assertEqual(br.triggered_projects, "data-hal,libinewrate")
+        JenkinsBuildInfo.objects.create(
+            job_url="http://fake.local/job/libinewrate-binaries/",
+            projectname="libinewrate",
+            jobname="libinewrate-binaries",
+            tag="UUIDA",
+            param_release=self.release,
+            param_release_uuid=self.release_uuid,
+            buildnumber=1,
+            result="SUCCESS",
+        )
+        br = BuildRelease.objects.get(pk=br.pk)
+        self.assertEqual(br.pool_size, 2)
+        self.assertEqual(br.triggered_projects, "data-hal,libinewrate")
+
+    @override_settings(BUILD_POOL=2)
+    def test_jbi_manage_pool_next(self, tb, dl):
+        self.test_jbi_manage_pool()
+        br = BuildRelease.objects.get(uuid=self.release_uuid)
+        self.assertEqual(br.pool_size, 2)
+        JenkinsBuildInfo.objects.create(
+            job_url="http://fake.local/job/data-hal-repos/",
+            projectname="data-hal",
+            jobname="data-hal-repos",
+            tag="UUIDA",
+            param_release=self.release,
+            param_release_uuid=self.release_uuid,
+            buildnumber=1,
+            result="SUCCESS",
+        )
+        br = BuildRelease.objects.get(pk=br.pk)
+        self.assertEqual(br.built_projects, "release-copy-debs-yml,data-hal")
+        params = {
+            "project": "libswrate-get-code",
+            "release_uuid": br.uuid,
+            "trigger_release": br.release,
+            "trigger_branch_or_tag": br.branch_or_tag,
+            "trigger_distribution": br.distribution,
+        }
+        tb.assert_called_once_with(**params)
+        self.assertEqual(br.pool_size, 2)
+        self.assertEqual(br.triggered_projects, "libinewrate,libswrate")
diff --git a/build/views.py b/build/views.py
index 7b652a1..a356255 100644
--- a/build/views.py
+++ b/build/views.py
@@ -63,11 +63,17 @@ class BuildReleaseDetail(generics.RetrieveDestroyAPIView):
 
     def patch(self, request, *args, **kwargs):
         action = request.data.get("action")
+        if action is None:
+            return JsonResponse({"error": "No action"}, status=400)
+        instance = self.get_object()
         if action == "refresh":
-            instance = self.get_object()
             instance.refresh_projects()
             serializer = self.get_serializer(instance)
             return Response(serializer.data)
+        elif action == "resume":
+            instance.resume()
+            serializer = self.get_serializer(instance)
+            return Response(serializer.data)
         return JsonResponse({"error": "Action unknown"}, status=400)
 
 
diff --git a/panel/static/panel/js/panel_release.js b/panel/static/panel/js/panel_release.js
index 235537e..c7dcca7 100644
--- a/panel/static/panel/js/panel_release.js
+++ b/panel/static/panel/js/panel_release.js
@@ -4,6 +4,45 @@ function click_retrigger( e, project ) {
     e.preventDefault();
 }
 
+/* eslint-disable-next-line no-unused-vars*/ // used at onClick
+function click_resume( e, id ) {
+    resume_build( id );
+    e.preventDefault();
+}
+
+function resume_build( id ) {
+
+  function successFunc( _data, _textStatus, _jqXHR ) {
+    $( "#resume" ).prop( "disabled", true );
+  }
+
+  function errorFunc( _jqXHR, _status, error ) {
+    $( "#release_error" ).html( error );
+  }
+  var csrftoken = jQuery( "[name=csrfmiddlewaretoken]" ).val();
+  function csrfSafeMethod( method ) {
+
+    // these HTTP methods do not require CSRF protection
+    return ( /^(GET|HEAD|OPTIONS|TRACE)$/.test( method ) );
+  }
+  $.ajaxSetup( {
+    beforeSend: function( xhr, settings ) {
+        if ( !csrfSafeMethod( settings.type ) && !this.crossDomain ) {
+            xhr.setRequestHeader( "X-CSRFToken", csrftoken );
+        }
+    }
+  } );
+  $.ajax( {
+    url: "/build/" + id + "/?format=json",
+    data: JSON.stringify( { action: "resume" } ),
+    method: "PATCH",
+    contentType: "application/json; charset=utf-8",
+    dataType: "json",
+    success: successFunc,
+    error: errorFunc
+  } );
+}
+
 function clean_all_uuids( project ) {
   for ( var uuid of $.release[ project ].uuids ) {
     $( "#" + project + "-" + uuid ).remove();
@@ -134,6 +173,19 @@ function is_project_done( project ) {
   return $.release[ project ][ uuid ].jobs.has( project + "-repos" );
 }
 
+
+function is_stuck() {
+  var success = parseInt( $( "#stats-success" ).text(), 10 );
+  var failed = parseInt( $( "#stats-danger" ).text(), 10 );
+  var queued = parseInt( $( "#stats-queued" ).text(), 10 );
+  var building = parseInt( $( "#stats-created" ).text(), 10 );
+
+  if ( failed === 0 && queued > 0 && building === 0 && success > 0 ) {
+    return true;
+  }
+  return false;
+}
+
 /* eslint-disable-next-line no-unused-vars */ // used on templates
 function update_release_info( release ) {
   if ( $.release.release_jobs.size < $.release.release_jobs_size ) {
@@ -147,4 +199,7 @@ function update_release_info( release ) {
       get_uuids_for_project( release, project );
     }
   }
+  if ( is_stuck() ) {
+    $( "#resume" ).prop( "disabled", false );
+  }
 }
diff --git a/panel/templates/panel/release_info.html b/panel/templates/panel/release_info.html
index 0e2bd80..fb7d53c 100644
--- a/panel/templates/panel/release_info.html
+++ b/panel/templates/panel/release_info.html
@@ -8,13 +8,21 @@
       <th>tag</th>
       <th>branch</th>
       <th>started_at</th>
+      <th>Action</th>
     </tr>
     <tr class="active">
       <td>{{ build_release.config.debian_release }}</td>
       <td>{{ build_release.tag }}</td>
       <td>{{ build_release.branch }}</td>
       <td>{{ build_release.start_date }}</td>
+      <td id="action_list">
+        <button type="button" id="resume"
+                disabled="disabled"
+                onclick="click_resume(event, '{{ build_release.id }}')"
+                class="btn btn-primary">Resume</button>
+      </td>
       </td>
     </tr>
   </table>
-</div>
\ No newline at end of file
+</div>
+<div class="panel-footer error" id="release_error"></div>
\ No newline at end of file