diff --git a/InvenTree/build/api.py b/InvenTree/build/api.py
index 89e929256c..295035bd99 100644
--- a/InvenTree/build/api.py
+++ b/InvenTree/build/api.py
@@ -1,8 +1,10 @@
 """JSON API for the Build app."""
 
 from django.urls import include, re_path
+from django.utils.translation import gettext_lazy as _
 
 from rest_framework import filters, generics
+from rest_framework.exceptions import ValidationError
 
 from django_filters.rest_framework import DjangoFilterBackend
 from django_filters import rest_framework as rest_filters
@@ -198,12 +200,24 @@ class BuildList(APIDownloadMixin, generics.ListCreateAPIView):
         return self.serializer_class(*args, **kwargs)
 
 
-class BuildDetail(generics.RetrieveUpdateAPIView):
+class BuildDetail(generics.RetrieveUpdateDestroyAPIView):
     """API endpoint for detail view of a Build object."""
 
     queryset = Build.objects.all()
     serializer_class = build.serializers.BuildSerializer
 
+    def destroy(self, request, *args, **kwargs):
+        """Only allow deletion of a BuildOrder if the build status is CANCELLED"""
+
+        build = self.get_object()
+
+        if build.status != BuildStatus.CANCELLED:
+            raise ValidationError({
+                "non_field_errors": [_("Build must be cancelled before it can be deleted")]
+            })
+
+        return super().destroy(request, *args, **kwargs)
+
 
 class BuildUnallocate(generics.CreateAPIView):
     """API endpoint for unallocating stock items from a build order.
diff --git a/InvenTree/build/templates/build/build_base.html b/InvenTree/build/templates/build/build_base.html
index 5da87bea7c..d006d0b6ab 100644
--- a/InvenTree/build/templates/build/build_base.html
+++ b/InvenTree/build/templates/build/build_base.html
@@ -249,9 +249,11 @@ src="{% static 'img/blank_image.png' %}"
     {% endif %}
 
     $("#build-delete").on('click', function() {
-        launchModalForm(
-            "{% url 'build-delete' build.id %}",
+        constructForm(
+            '{% url "api-build-detail" build.pk %}',
             {
+                method: 'DELETE',
+                title: '{% trans "Delete Build Order" %}',
                 redirect: "{% url 'build-index' %}",
             }
         );
diff --git a/InvenTree/build/templates/build/delete_build.html b/InvenTree/build/templates/build/delete_build.html
deleted file mode 100644
index 62dab01da0..0000000000
--- a/InvenTree/build/templates/build/delete_build.html
+++ /dev/null
@@ -1,7 +0,0 @@
-{% extends "modal_delete_form.html" %}
-{% load i18n %}
-{% block pre_form_content %}
-
-{% trans "Are you sure you want to delete this build?" %}
-
-{% endblock %}
diff --git a/InvenTree/build/test_api.py b/InvenTree/build/test_api.py
index c565506eae..abd25e7d42 100644
--- a/InvenTree/build/test_api.py
+++ b/InvenTree/build/test_api.py
@@ -90,7 +90,7 @@ class BuildAPITest(InvenTreeAPITestCase):
     # Required roles to access Build API endpoints
     roles = [
         'build.change',
-        'build.add'
+        'build.add',
     ]
 
 
@@ -268,6 +268,39 @@ class BuildTest(BuildAPITest):
 
         self.assertEqual(bo.status, BuildStatus.CANCELLED)
 
+    def test_delete(self):
+        """Test that we can delete a BuildOrder via the API"""
+
+        bo = Build.objects.get(pk=1)
+
+        url = reverse('api-build-detail', kwargs={'pk': bo.pk})
+
+        # At first we do not have the required permissions
+        self.delete(
+            url,
+            expected_code=403,
+        )
+
+        self.assignRole('build.delete')
+
+        # As build is currently not 'cancelled', it cannot be deleted
+        self.delete(
+            url,
+            expected_code=400,
+        )
+
+        bo.status = BuildStatus.CANCELLED
+        bo.save()
+
+        # Now, we should be able to delete
+        self.delete(
+            url,
+            expected_code=204,
+        )
+
+        with self.assertRaises(Build.DoesNotExist):
+            Build.objects.get(pk=1)
+
     def test_create_delete_output(self):
         """Test that we can create and delete build outputs via the API."""
         bo = Build.objects.get(pk=1)
diff --git a/InvenTree/build/urls.py b/InvenTree/build/urls.py
index b524df5627..0b33c7d78e 100644
--- a/InvenTree/build/urls.py
+++ b/InvenTree/build/urls.py
@@ -4,15 +4,12 @@ from django.urls import include, re_path
 
 from . import views
 
-build_detail_urls = [
-    re_path(r'^delete/', views.BuildDelete.as_view(), name='build-delete'),
-
-    re_path(r'^.*$', views.BuildDetail.as_view(), name='build-detail'),
-]
 
 build_urls = [
 
-    re_path(r'^(?P<pk>\d+)/', include(build_detail_urls)),
+    re_path(r'^(?P<pk>\d+)/', include([
+        re_path(r'^.*$', views.BuildDetail.as_view(), name='build-detail'),
+    ])),
 
     re_path(r'.*$', views.BuildIndex.as_view(), name='build-index'),
 ]
diff --git a/InvenTree/build/views.py b/InvenTree/build/views.py
index 9d01ddc3d6..bccd1b31e6 100644
--- a/InvenTree/build/views.py
+++ b/InvenTree/build/views.py
@@ -1,11 +1,9 @@
 """Django views for interacting with Build objects."""
 
-from django.utils.translation import gettext_lazy as _
 from django.views.generic import DetailView, ListView
 
 from .models import Build
 
-from InvenTree.views import AjaxDeleteView
 from InvenTree.views import InvenTreeRoleMixin
 from InvenTree.status_codes import BuildStatus
 
@@ -49,11 +47,3 @@ class BuildDetail(InvenTreeRoleMixin, InvenTreePluginViewMixin, DetailView):
         ctx['has_untracked_bom_items'] = build.has_untracked_bom_items()
 
         return ctx
-
-
-class BuildDelete(AjaxDeleteView):
-    """View to delete a build."""
-
-    model = Build
-    ajax_template_name = 'build/delete_build.html'
-    ajax_form_title = _('Delete Build Order')