diff --git a/InvenTree/InvenTree/metadata.py b/InvenTree/InvenTree/metadata.py
index e0f8a23322..792714e439 100644
--- a/InvenTree/InvenTree/metadata.py
+++ b/InvenTree/InvenTree/metadata.py
@@ -37,6 +37,25 @@ class InvenTreeMetadata(SimpleMetadata):
 
         metadata = super().determine_metadata(request, view)
 
+        """
+        Custom context information to pass through to the OPTIONS endpoint,
+        if the "context=True" is supplied to the OPTIONS requst
+        
+        Serializer class can supply either:
+
+        - get_context_data() (method)
+        - CONTEXT_DATA (dict)
+        """
+
+        context = {}
+
+        if hasattr(self.serializer, 'get_context_data'):
+            context = self.serializer.get_context_data()
+        elif hasattr(self.erializer, 'CONTEXT_DATA'):
+            context = self.serializer.CONTEXT_DATA
+
+        metadata['context'] = context
+
         user = request.user
 
         if user is None:
@@ -99,6 +118,8 @@ class InvenTreeMetadata(SimpleMetadata):
         to any fields whose Meta.model specifies a default value
         """
 
+        self.serializer = serializer
+
         serializer_info = super().get_serializer_info(serializer)
 
         model_class = None
diff --git a/InvenTree/order/api.py b/InvenTree/order/api.py
index a54c370121..12ceb600fc 100644
--- a/InvenTree/order/api.py
+++ b/InvenTree/order/api.py
@@ -286,7 +286,37 @@ class PurchaseOrderDetail(generics.RetrieveUpdateDestroyAPIView):
         return queryset
 
 
-class PurchaseOrderReceive(generics.CreateAPIView):
+class PurchaseOrderContextMixin:
+
+    def get_serializer_context(self):
+        """ Add the PurchaseOrder object to the serializer context """
+
+        context = super().get_serializer_context()
+
+        # Pass the purchase order through to the serializer for validation
+        try:
+            context['order'] = models.PurchaseOrder.objects.get(pk=self.kwargs.get('pk', None))
+        except:
+            pass
+
+        context['request'] = self.request
+
+        return context
+
+
+class PurchaseOrderCancel(PurchaseOrderContextMixin, generics.CreateAPIView):
+    """
+    API endpoint to 'cancel' a purchase order.
+
+    The purchase order must be in a state which can be cancelled
+    """
+
+    queryset = models.PurchaseOrderLineItem.objects.all()
+
+    serializer_class = serializers.PurchaseOrderCancelSerializer
+
+
+class PurchaseOrderReceive(PurchaseOrderContextMixin, generics.CreateAPIView):
     """
     API endpoint to receive stock items against a purchase order.
 
@@ -303,20 +333,6 @@ class PurchaseOrderReceive(generics.CreateAPIView):
 
     serializer_class = serializers.PurchaseOrderReceiveSerializer
 
-    def get_serializer_context(self):
-
-        context = super().get_serializer_context()
-
-        # Pass the purchase order through to the serializer for validation
-        try:
-            context['order'] = models.PurchaseOrder.objects.get(pk=self.kwargs.get('pk', None))
-        except:
-            pass
-
-        context['request'] = self.request
-
-        return context
-
 
 class PurchaseOrderLineItemFilter(rest_filters.FilterSet):
     """
@@ -1107,6 +1123,7 @@ order_api_urls = [
         # Individual purchase order detail URLs
         re_path(r'^(?P<pk>\d+)/', include([
             re_path(r'^receive/', PurchaseOrderReceive.as_view(), name='api-po-receive'),
+            re_path(r'^cancel/', PurchaseOrderCancel.as_view(), name='api-po-cancel'),
             re_path(r'.*$', PurchaseOrderDetail.as_view(), name='api-po-detail'),
         ])),
 
diff --git a/InvenTree/order/models.py b/InvenTree/order/models.py
index 2b722ddecd..af1b159afa 100644
--- a/InvenTree/order/models.py
+++ b/InvenTree/order/models.py
@@ -380,6 +380,7 @@ class PurchaseOrder(Order):
             PurchaseOrderStatus.PENDING
         ]
 
+    @transaction.atomic
     def cancel_order(self):
         """ Marks the PurchaseOrder as CANCELLED. """
 
diff --git a/InvenTree/order/serializers.py b/InvenTree/order/serializers.py
index 7d26ce741d..a151da77b9 100644
--- a/InvenTree/order/serializers.py
+++ b/InvenTree/order/serializers.py
@@ -179,6 +179,35 @@ class PurchaseOrderSerializer(AbstractOrderSerializer, ReferenceIndexingSerializ
         ]
 
 
+class PurchaseOrderCancelSerializer(serializers.Serializer):
+    """
+    Serializer for cancelling a PurchaseOrder
+    """
+
+    class Meta:
+        fields = [],
+
+    def get_context_data(self):
+        """
+        Return custom context information about the order
+        """
+
+        self.order = self.context['order']
+
+        return {
+            'can_cancel': self.order.can_cancel(),
+        }
+
+    def save(self):
+
+        order = self.context['order']
+
+        if not order.can_cancel():
+            raise ValidationError(_("Order cannot be cancelled"))
+        
+        order.cancel_order()
+
+
 class PurchaseOrderLineItemSerializer(InvenTreeModelSerializer):
 
     @staticmethod