diff --git a/src/Extensions/src/Eventuous.AspNetCore.Web/Http/CommandHttpApiBase'TResult.cs b/src/Extensions/src/Eventuous.AspNetCore.Web/Http/CommandHttpApiBase'TResult.cs
index 3cb3c7129..2016e02a0 100644
--- a/src/Extensions/src/Eventuous.AspNetCore.Web/Http/CommandHttpApiBase'TResult.cs
+++ b/src/Extensions/src/Eventuous.AspNetCore.Web/Http/CommandHttpApiBase'TResult.cs
@@ -1,6 +1,9 @@
 // Copyright (C) Ubiquitous AS.All rights reserved
 // Licensed under the Apache License, Version 2.0.
 
+using Microsoft.AspNetCore.Mvc.Formatters;
+using Microsoft.AspNetCore.Http;
+
 namespace Eventuous.AspNetCore.Web; 
 
 /// <summary>
@@ -46,12 +49,40 @@ protected async Task<ActionResult<TResult>> Handle<TContract, TCommand>(TContrac
         return AsActionResult<TAggregate>(result);
     }
 
-    static ActionResult<TResult> AsActionResult<T>(Result result) where T : Aggregate
-        => result is ErrorResult error
+    protected virtual ActionResult<TResult> AsActionResult<T>(Result result) where T : Aggregate {
+        return result is ErrorResult error
             ? error.Exception switch {
-                OptimisticConcurrencyException<T> => new ConflictObjectResult(error),
-                AggregateNotFoundException<T>     => new NotFoundObjectResult(error),
-                _                                 => new BadRequestObjectResult(error)
+                OptimisticConcurrencyException<T> => AsProblemResult(StatusCodes.Status409Conflict),
+                AggregateNotFoundException<T>     => AsProblemResult(StatusCodes.Status404NotFound),
+                DomainException                   => AsValidationProblemResult(StatusCodes.Status400BadRequest),
+                _                                 => AsProblemResult(StatusCodes.Status500InternalServerError)
             }
             : new OkObjectResult(result);
+
+        ActionResult AsProblemResult(int statusCode)
+            => new ObjectResult(
+                new ProblemDetails {
+                    Status = statusCode,
+                    Title = error.ErrorMessage,
+                    Detail = error.Exception?.ToString(),
+                    Type = error.Exception?.GetType().Name
+                }
+            ) {
+                StatusCode = StatusCodes.Status400BadRequest,
+                ContentTypes = new MediaTypeCollection { ContentTypes.ProblemDetails },
+            };
+
+        ActionResult AsValidationProblemResult(int statusCode)
+            => new ObjectResult(
+                new ValidationProblemDetails(new Dictionary<string, string[]> { ["Domain"] = [error.ErrorMessage] }) {
+                    Status = statusCode,
+                    Title = error.ErrorMessage,
+                    Detail = error.Exception?.ToString(),
+                    Type = error.Exception?.GetType().Name
+                }
+            ) {
+                StatusCode = StatusCodes.Status400BadRequest,
+                ContentTypes = new MediaTypeCollection { ContentTypes.ProblemDetails },
+            };
+    }
 }
diff --git a/src/Extensions/src/Eventuous.AspNetCore.Web/Http/CommandHttpApiBase.cs b/src/Extensions/src/Eventuous.AspNetCore.Web/Http/CommandHttpApiBase.cs
index 006040552..d86e6711a 100644
--- a/src/Extensions/src/Eventuous.AspNetCore.Web/Http/CommandHttpApiBase.cs
+++ b/src/Extensions/src/Eventuous.AspNetCore.Web/Http/CommandHttpApiBase.cs
@@ -1,6 +1,9 @@
 // Copyright (C) Ubiquitous AS. All rights reserved
 // Licensed under the Apache License, Version 2.0.
 
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Mvc.Formatters;
+
 namespace Eventuous.AspNetCore.Web;
 
 /// <summary>
@@ -44,12 +47,40 @@ protected async Task<ActionResult> Handle<TContract, TCommand>(TContract httpCom
         return AsActionResult<TAggregate>(result);
     }
 
-    static ActionResult AsActionResult<T>(Result result) where T : Aggregate
-        => result is ErrorResult error
+    protected virtual ActionResult AsActionResult<T>(Result result) where T : Aggregate {
+        return result is ErrorResult error
             ? error.Exception switch {
-                OptimisticConcurrencyException<T> => new ConflictObjectResult(error),
-                AggregateNotFoundException<T>     => new NotFoundObjectResult(error),
-                _                                 => new BadRequestObjectResult(error)
+                OptimisticConcurrencyException<T> => AsProblemResult(StatusCodes.Status409Conflict),
+                AggregateNotFoundException<T>     => AsProblemResult(StatusCodes.Status404NotFound),
+                DomainException                   => AsValidationProblemResult(StatusCodes.Status400BadRequest),
+                _                                 => AsProblemResult(StatusCodes.Status500InternalServerError)
             }
             : new OkObjectResult(result);
+
+        ActionResult AsProblemResult(int statusCode)
+            => new ObjectResult(
+                new ProblemDetails {
+                    Status = statusCode,
+                    Title = error.ErrorMessage,
+                    Detail = error.Exception?.ToString(),
+                    Type = error.Exception?.GetType().Name
+                }
+            ) {
+                StatusCode = StatusCodes.Status400BadRequest,
+                ContentTypes = new MediaTypeCollection { ContentTypes.ProblemDetails },
+            };
+
+        ActionResult AsValidationProblemResult(int statusCode)
+            => new ObjectResult(
+                new ValidationProblemDetails(new Dictionary<string, string[]> { ["Domain"] = [error.ErrorMessage] }) {
+                    Status = statusCode,
+                    Title = error.ErrorMessage,
+                    Detail = error.Exception?.ToString(),
+                    Type = error.Exception?.GetType().Name
+                }
+            ) {
+                StatusCode = StatusCodes.Status400BadRequest,
+                ContentTypes = new MediaTypeCollection { ContentTypes.ProblemDetails },
+            };
+    }
 }
diff --git a/src/Extensions/src/Eventuous.AspNetCore.Web/Http/HttpCommandMapping.cs b/src/Extensions/src/Eventuous.AspNetCore.Web/Http/HttpCommandMapping.cs
index 82177c736..b58cb5cbc 100644
--- a/src/Extensions/src/Eventuous.AspNetCore.Web/Http/HttpCommandMapping.cs
+++ b/src/Extensions/src/Eventuous.AspNetCore.Web/Http/HttpCommandMapping.cs
@@ -175,7 +175,7 @@ async Task<IResult> (HttpContext context, ICommandService<TAggregate> service) =
             .ProducesProblemDetails(Status404NotFound)
             .ProducesProblemDetails(Status409Conflict)
             .ProducesProblemDetails(Status500InternalServerError)
-            .ProducesProblemDetails(Status400BadRequest);
+            .ProducesValidationProblemDetails(Status400BadRequest);
 
         routeBuilder.AddPolicy(policyName);
         routeBuilder.AddAuthorization(typeof(TContract));
@@ -247,7 +247,7 @@ async Task<IResult> (HttpContext context) => {
                 .ProducesOk(resultType)
                 .ProducesProblemDetails(Status404NotFound)
                 .ProducesProblemDetails(Status409Conflict)
-                .ProducesProblemDetails(Status400BadRequest)
+                .ProducesValidationProblemDetails(Status400BadRequest)
                 .ProducesProblemDetails(Status500InternalServerError);
 
             routeBuilder.AddPolicy(policyName);
diff --git a/src/Extensions/src/Eventuous.AspNetCore.Web/Http/ResultExtensions.cs b/src/Extensions/src/Eventuous.AspNetCore.Web/Http/ResultExtensions.cs
index 8453c7e7a..e24ab59a7 100644
--- a/src/Extensions/src/Eventuous.AspNetCore.Web/Http/ResultExtensions.cs
+++ b/src/Extensions/src/Eventuous.AspNetCore.Web/Http/ResultExtensions.cs
@@ -13,7 +13,7 @@ public static IResult AsResult<TResult>(this Result result) where TResult : Resu
             ? error.Exception switch {
                 OptimisticConcurrencyException => AsProblemDetails(Status409Conflict, error),
                 AggregateNotFoundException     => AsProblemDetails(Status404NotFound, error),
-                DomainException                => AsProblemDetails(Status400BadRequest, error),
+                DomainException                => AsValidationProblemDetails(Status400BadRequest, error),
                 _                              => AsProblemDetails(Status500InternalServerError, error)
             }
             : Results.Ok(result);
@@ -29,12 +29,21 @@ static IResult AsProblemDetails(int statusCode, ErrorResult error)
             }
         );
 
+    static IResult AsValidationProblemDetails(int statusCode, ErrorResult error)
+        => Results.ValidationProblem(
+            errors: new Dictionary<string, string[]> { ["Domain"] = [error.ErrorMessage] },
+            statusCode: statusCode,
+            title: error.ErrorMessage,
+            detail: error.Exception?.ToString(),
+            type: error.Exception?.GetType().Name
+        );
+
     public static ActionResult<Result<T>> AsActionResult<T>(this Result result) where T : State<T>, new() {
         return result is ErrorResult error
             ? error.Exception switch {
                 OptimisticConcurrencyException => AsProblemResult(Status409Conflict),
                 AggregateNotFoundException     => AsProblemResult(Status404NotFound),
-                DomainException                => AsProblemResult(Status400BadRequest),
+                DomainException                => AsValidationProblemResult(Status400BadRequest),
                 _                              => AsProblemResult(Status500InternalServerError)
             }
             : new OkObjectResult(result);
@@ -51,5 +60,18 @@ ActionResult AsProblemResult(int statusCode)
                 StatusCode   = Status400BadRequest,
                 ContentTypes = new MediaTypeCollection { ContentTypes.ProblemDetails },
             };
+
+        ActionResult AsValidationProblemResult(int statusCode)
+            => new ObjectResult(
+                new ValidationProblemDetails(new Dictionary<string, string[]> { ["Domain"] = [error.ErrorMessage] }) {
+                    Status = statusCode,
+                    Title  = error.ErrorMessage,
+                    Detail = error.Exception?.ToString(),
+                    Type   = error.Exception?.GetType().Name
+                }
+            ) {
+                StatusCode   = Status400BadRequest,
+                ContentTypes = new MediaTypeCollection { ContentTypes.ProblemDetails },
+            };
     }
 }
diff --git a/src/Extensions/src/Eventuous.AspNetCore.Web/Http/RouteHandlerBuilderExt.cs b/src/Extensions/src/Eventuous.AspNetCore.Web/Http/RouteHandlerBuilderExt.cs
index c1d0aed36..6525709a5 100644
--- a/src/Extensions/src/Eventuous.AspNetCore.Web/Http/RouteHandlerBuilderExt.cs
+++ b/src/Extensions/src/Eventuous.AspNetCore.Web/Http/RouteHandlerBuilderExt.cs
@@ -7,18 +7,21 @@
 namespace Eventuous.AspNetCore.Web;
 
 static class RouteHandlerBuilderExt {
+    public static RouteHandlerBuilder ProducesValidationProblemDetails(this RouteHandlerBuilder builder, int statusCode)
+        => builder.Produces<ValidationProblemDetails>(statusCode, ContentTypes.ProblemDetails);
+
     public static RouteHandlerBuilder ProducesProblemDetails(this RouteHandlerBuilder builder, int statusCode)
         => builder.Produces<ProblemDetails>(statusCode, ContentTypes.ProblemDetails);
 
     public static RouteHandlerBuilder ProducesOk(this RouteHandlerBuilder builder, Type resultType)
         => builder.Produces(StatusCodes.Status200OK, resultType, ContentTypes.Json);
-    
+
     public static RouteHandlerBuilder ProducesOk<T>(this RouteHandlerBuilder builder) where T : Result
         => builder.ProducesOk(typeof(T));
 
     public static RouteHandlerBuilder Accepts(this RouteHandlerBuilder builder, Type commandType)
         => builder.Accepts(commandType, false, ContentTypes.Json);
-    
+
     public static RouteHandlerBuilder Accepts<T>(this RouteHandlerBuilder builder)
         => builder.Accepts(typeof(T));
 }
