diff --git a/apidocs/openapi.yaml b/apidocs/openapi.yaml
index 9b965c5633c..ab932931706 100644
--- a/apidocs/openapi.yaml
+++ b/apidocs/openapi.yaml
@@ -15,6 +15,12 @@ security: []
 
 components:
   schemas:
+    Error:
+      type: object
+      properties:
+        error:
+          type: string
+
     GlobalConf:
       type: object
       properties:
@@ -633,8 +639,16 @@ paths:
                 $ref: '#/components/schemas/GlobalConf'
         '400':
           description: invalid request.
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Error'
         '500':
           description: server error.
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Error'
 
   /v3/config/global/patch:
     patch:
@@ -652,8 +666,16 @@ paths:
           description: the request was successful.
         '400':
           description: invalid request.
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Error'
         '500':
           description: server error.
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Error'
 
   /v3/config/pathdefaults/get:
     get:
@@ -669,8 +691,16 @@ paths:
                 $ref: '#/components/schemas/PathConf'
         '400':
           description: invalid request.
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Error'
         '500':
           description: server error.
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Error'
 
   /v3/config/pathdefaults/patch:
     patch:
@@ -688,8 +718,16 @@ paths:
           description: the request was successful.
         '400':
           description: invalid request.
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Error'
         '500':
           description: server error.
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Error'
 
   /v3/config/paths/list:
     get:
@@ -718,8 +756,16 @@ paths:
                 $ref: '#/components/schemas/PathConfList'
         '400':
           description: invalid request.
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Error'
         '500':
           description: server error.
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Error'
 
   /v3/config/paths/get/{name}:
     get:
@@ -742,8 +788,16 @@ paths:
                 $ref: '#/components/schemas/PathConf'
         '400':
           description: invalid request.
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Error'
         '500':
           description: server error.
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Error'
 
   /v3/config/paths/add/{name}:
     post:
@@ -768,8 +822,16 @@ paths:
           description: the request was successful.
         '400':
           description: invalid request.
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Error'
         '500':
           description: server error.
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Error'
 
   /v3/config/paths/patch/{name}:
     patch:
@@ -794,8 +856,16 @@ paths:
           description: the request was successful.
         '400':
           description: invalid request.
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Error'
         '500':
           description: server error.
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Error'
 
   /v3/config/paths/replace/{name}:
     post:
@@ -820,8 +890,16 @@ paths:
           description: the request was successful.
         '400':
           description: invalid request.
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Error'
         '500':
           description: server error.
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Error'
 
   /v3/config/paths/delete/{name}:
     delete:
@@ -840,8 +918,16 @@ paths:
           description: the request was successful.
         '400':
           description: invalid request.
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Error'
         '500':
           description: server error.
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Error'
 
   /v3/hlsmuxers/list:
     get:
@@ -870,8 +956,16 @@ paths:
                 $ref: '#/components/schemas/HLSMuxerList'
         '400':
           description: invalid request.
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Error'
         '500':
           description: server error.
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Error'
 
   /v3/hlsmuxers/get/{name}:
     get:
@@ -894,8 +988,16 @@ paths:
                 $ref: '#/components/schemas/HLSMuxer'
         '400':
           description: invalid request.
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Error'
         '500':
           description: server error.
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Error'
 
   /v3/paths/list:
     get:
@@ -924,8 +1026,16 @@ paths:
                 $ref: '#/components/schemas/PathList'
         '400':
           description: invalid request.
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Error'
         '500':
           description: server error.
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Error'
 
   /v3/paths/get/{name}:
     get:
@@ -948,8 +1058,16 @@ paths:
                 $ref: '#/components/schemas/Path'
         '400':
           description: invalid request.
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Error'
         '500':
           description: server error.
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Error'
 
   /v3/rtspconns/list:
     get:
@@ -978,8 +1096,16 @@ paths:
                 $ref: '#/components/schemas/RTSPConnList'
         '400':
           description: invalid request.
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Error'
         '500':
           description: server error.
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Error'
 
   /v3/rtspconns/get/{id}:
     get:
@@ -1002,8 +1128,16 @@ paths:
                 $ref: '#/components/schemas/RTSPConn'
         '400':
           description: invalid request.
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Error'
         '500':
           description: server error.
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Error'
 
   /v3/rtspsessions/list:
     get:
@@ -1032,8 +1166,16 @@ paths:
                 $ref: '#/components/schemas/RTSPSessionList'
         '400':
           description: invalid request.
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Error'
         '500':
           description: server error.
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Error'
 
   /v3/rtspsessions/get/{id}:
     get:
@@ -1056,8 +1198,16 @@ paths:
                 $ref: '#/components/schemas/RTSPSession'
         '400':
           description: invalid request.
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Error'
         '500':
           description: server error.
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Error'
 
   /v3/rtspsessions/kick/{id}:
     post:
@@ -1076,8 +1226,16 @@ paths:
           description: the request was successful.
         '400':
           description: invalid request.
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Error'
         '500':
           description: server error.
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Error'
 
   /v3/rtspsconns/list:
     get:
@@ -1106,8 +1264,16 @@ paths:
                 $ref: '#/components/schemas/RTSPConnList'
         '400':
           description: invalid request.
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Error'
         '500':
           description: server error.
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Error'
 
   /v3/rtspsconns/get/{id}:
     get:
@@ -1130,8 +1296,16 @@ paths:
                 $ref: '#/components/schemas/RTSPConn'
         '400':
           description: invalid request.
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Error'
         '500':
           description: server error.
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Error'
 
   /v3/rtspssessions/list:
     get:
@@ -1160,8 +1334,16 @@ paths:
                 $ref: '#/components/schemas/RTSPSessionList'
         '400':
           description: invalid request.
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Error'
         '500':
           description: server error.
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Error'
 
   /v3/rtspssessions/get/{id}:
     get:
@@ -1184,8 +1366,16 @@ paths:
                 $ref: '#/components/schemas/RTSPSession'
         '400':
           description: invalid request.
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Error'
         '500':
           description: server error.
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Error'
 
   /v3/rtspssessions/kick/{id}:
     post:
@@ -1204,8 +1394,16 @@ paths:
           description: the request was successful.
         '400':
           description: invalid request.
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Error'
         '500':
           description: server error.
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Error'
 
   /v3/rtmpconns/list:
     get:
@@ -1234,8 +1432,16 @@ paths:
                 $ref: '#/components/schemas/RTMPConnList'
         '400':
           description: invalid request.
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Error'
         '500':
           description: server error.
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Error'
 
   /v3/rtmpconns/get/{id}:
     get:
@@ -1258,8 +1464,16 @@ paths:
                 $ref: '#/components/schemas/RTMPConn'
         '400':
           description: invalid request.
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Error'
         '500':
           description: server error.
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Error'
 
   /v3/rtmpconns/kick/{id}:
     post:
@@ -1278,8 +1492,16 @@ paths:
           description: the request was successful.
         '400':
           description: invalid request.
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Error'
         '500':
           description: server error.
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Error'
 
   /v3/rtmpsconns/list:
     get:
@@ -1308,8 +1530,16 @@ paths:
                 $ref: '#/components/schemas/RTMPConnList'
         '400':
           description: invalid request.
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Error'
         '500':
           description: server error.
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Error'
 
   /v3/rtmpsconns/get/{id}:
     get:
@@ -1332,8 +1562,16 @@ paths:
                 $ref: '#/components/schemas/RTMPConn'
         '400':
           description: invalid request.
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Error'
         '500':
           description: server error.
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Error'
 
   /v3/rtmpsconns/kick/{id}:
     post:
@@ -1352,8 +1590,16 @@ paths:
           description: the request was successful.
         '400':
           description: invalid request.
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Error'
         '500':
           description: server error.
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Error'
 
   /v3/srtconns/list:
     get:
@@ -1382,8 +1628,16 @@ paths:
                 $ref: '#/components/schemas/SRTConnList'
         '400':
           description: invalid request.
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Error'
         '500':
           description: server error.
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Error'
 
   /v3/srtconns/get/{id}:
     get:
@@ -1406,8 +1660,16 @@ paths:
                 $ref: '#/components/schemas/SRTConn'
         '400':
           description: invalid request.
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Error'
         '500':
           description: server error.
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Error'
 
   /v3/srtconns/kick/{id}:
     post:
@@ -1426,8 +1688,16 @@ paths:
           description: the request was successful.
         '400':
           description: invalid request.
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Error'
         '500':
           description: server error.
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Error'
 
   /v3/webrtcsessions/list:
     get:
@@ -1456,8 +1726,16 @@ paths:
                 $ref: '#/components/schemas/WebRTCSessionList'
         '400':
           description: invalid request.
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Error'
         '500':
           description: server error.
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Error'
 
   /v3/webrtcsessions/get/{id}:
     get:
@@ -1480,8 +1758,16 @@ paths:
                 $ref: '#/components/schemas/WebRTCSession'
         '400':
           description: invalid request.
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Error'
         '500':
           description: server error.
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Error'
 
   /v3/webrtcsessions/kick/{id}:
     post:
@@ -1500,5 +1786,13 @@ paths:
           description: the request was successful.
         '400':
           description: invalid request.
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Error'
         '500':
           description: server error.
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/Error'
diff --git a/internal/core/api.go b/internal/core/api.go
index 098bad891ce..7be9c81149d 100644
--- a/internal/core/api.go
+++ b/internal/core/api.go
@@ -277,14 +277,24 @@ func (a *api) Log(level logger.Level, format string, args ...interface{}) {
 
 // error coming from something the user inserted into the request.
 func (a *api) writeUserError(ctx *gin.Context, err error) {
+	// show error in logs
 	a.Log(logger.Error, err.Error())
-	ctx.AbortWithStatus(http.StatusBadRequest)
+
+	// send error in response
+	ctx.JSON(http.StatusBadRequest, &apiError{
+		Error: err.Error(),
+	})
 }
 
 // error coming from the server.
 func (a *api) writeServerError(ctx *gin.Context, err error) {
+	// show error in logs
 	a.Log(logger.Error, err.Error())
-	ctx.AbortWithStatus(http.StatusInternalServerError)
+
+	// send error in response
+	ctx.JSON(http.StatusInternalServerError, &apiError{
+		Error: err.Error(),
+	})
 }
 
 func (a *api) onConfigGlobalGet(ctx *gin.Context) {
diff --git a/internal/core/api_defs.go b/internal/core/api_defs.go
index c791507be0f..edf6036ad68 100644
--- a/internal/core/api_defs.go
+++ b/internal/core/api_defs.go
@@ -8,6 +8,10 @@ import (
 	"github.com/bluenviron/mediamtx/internal/conf"
 )
 
+type apiError struct {
+	Error string `json:"error"`
+}
+
 type apiPathConfList struct {
 	ItemCount int                  `json:"itemCount"`
 	PageCount int                  `json:"pageCount"`
diff --git a/internal/core/api_test.go b/internal/core/api_test.go
index 2dbbc2b55f4..f45ecdfa8ef 100644
--- a/internal/core/api_test.go
+++ b/internal/core/api_test.go
@@ -64,6 +64,13 @@ func checkClose(t *testing.T, closeFunc func() error) {
 	require.NoError(t, closeFunc())
 }
 
+func checkError(t *testing.T, msg string, body io.Reader) {
+	var resErr map[string]interface{}
+	err := json.NewDecoder(body).Decode(&resErr)
+	require.NoError(t, err)
+	require.Equal(t, map[string]interface{}{"error": msg}, resErr)
+}
+
 func httpRequest(t *testing.T, hc *http.Client, method string, ur string, in interface{}, out interface{}) {
 	buf := func() io.Reader {
 		if in == nil {
@@ -184,7 +191,9 @@ func TestAPIConfigGlobalPatchUnknownField(t *testing.T) {
 		res, err := hc.Do(req)
 		require.NoError(t, err)
 		defer res.Body.Close()
+
 		require.Equal(t, http.StatusBadRequest, res.StatusCode)
+		checkError(t, "json: unknown field \"test\"", res.Body)
 	}()
 }
 
@@ -320,7 +329,9 @@ func TestAPIConfigPathsAddUnknownField(t *testing.T) {
 		res, err := hc.Do(req)
 		require.NoError(t, err)
 		defer res.Body.Close()
+
 		require.Equal(t, http.StatusBadRequest, res.StatusCode)
+		checkError(t, "json: unknown field \"test\"", res.Body)
 	}()
 }
 
@@ -398,7 +409,9 @@ func TestAPIConfigPathsDelete(t *testing.T) {
 		res, err := hc.Do(req)
 		require.NoError(t, err)
 		defer res.Body.Close()
+
 		require.Equal(t, http.StatusInternalServerError, res.StatusCode)
+		checkError(t, "path configuration not found", res.Body)
 	}()
 }
 
@@ -650,7 +663,9 @@ func TestAPIPathsGet(t *testing.T) {
 				res, err := hc.Get("http://localhost:9997/v3/paths/get/" + pathName)
 				require.NoError(t, err)
 				defer res.Body.Close()
+
 				require.Equal(t, http.StatusInternalServerError, res.StatusCode)
+				checkError(t, "path not found", res.Body)
 			}
 		})
 	}
@@ -1331,7 +1346,19 @@ func TestAPIProtocolGetNotFound(t *testing.T) {
 				res, err := hc.Do(req)
 				require.NoError(t, err)
 				defer res.Body.Close()
+
 				require.Equal(t, http.StatusInternalServerError, res.StatusCode)
+
+				switch ca {
+				case "rtsp conns", "rtsps conns", "rtmp", "rtmps", "srt":
+					checkError(t, "connection not found", res.Body)
+
+				case "rtsp sessions", "rtsps sessions", "webrtc":
+					checkError(t, "session not found", res.Body)
+
+				case "hls":
+					checkError(t, "muxer not found", res.Body)
+				}
 			}()
 		})
 	}
@@ -1543,7 +1570,19 @@ func TestAPIProtocolKickNotFound(t *testing.T) {
 				res, err := hc.Do(req)
 				require.NoError(t, err)
 				defer res.Body.Close()
+
 				require.Equal(t, http.StatusInternalServerError, res.StatusCode)
+
+				switch ca {
+				case "rtsp conns", "rtsps conns", "rtmp", "rtmps", "srt":
+					checkError(t, "connection not found", res.Body)
+
+				case "rtsp sessions", "rtsps sessions", "webrtc":
+					checkError(t, "session not found", res.Body)
+
+				case "hls":
+					checkError(t, "muxer not found", res.Body)
+				}
 			}()
 		})
 	}