diff --git a/docs/README_server.md b/docs/README_server.md
index 814f0e59a4720d7e16559eba98b4d2c1bb6e57c4..8b4fbc555a6f0130ab3e8c532ed6ce606da43e00 100644
--- a/docs/README_server.md
+++ b/docs/README_server.md
@@ -366,14 +366,15 @@ These capabilities are called "VM access capabilities":
 
 ### Template management
 
-| Route                  | Description                | Method | Parameters       | Req. user cap.                     |
-|---                     |---                         |---     |---               |---                                 |
-| `/templates/vm`        | create a template          | POST   | vmID,name,access | `TPL_CREATE`                       |
-| `/templates/qcow`      | create a template          | POST   | qcow,name,access | `TPL_CREATE`                       |
-| `/templates/{id}`      | edit a template            | PUT    | name,access      | `TPL_EDIT_ANY` OR `TPL_EDIT`       |
-| `/templates/{id}`      | delete a template          | DELETE |                  | `TPL_DESTROY_ANY` OR `TPL_DESTROY` |
-| `/templates/{id}/disk` | download a template's disk | GET    |                  | `TPL_READFS_ANY` OR `TPL_READFS`   |
-| `/templates`           | list templates             | GET    |                  | `TPL_LIST_ANY` OR `TPL_LIST`       |
+| Route                  | Description                          | Method | Parameters       | Req. user cap.                     |
+|---                     |---                                   |---     |---               |---                                 |
+| `/templates`           | returns templates that can be listed | GET    |                  | `TPL_LIST_ANY` OR `TPL_LIST`       |
+| `/templates/{id}`      | returns a template                   | GET    |                  | `TPL_LIST_ANY` OR `TPL_LIST`       |
+| `/templates/vm`        | create a template                    | POST   | vmID,name,access | `TPL_CREATE`                       |
+| `/templates/qcow`      | create a template                    | POST   | qcow,name,access | `TPL_CREATE`                       |
+| `/templates/{id}`      | edit a template                      | PUT    | name,access      | `TPL_EDIT_ANY` OR `TPL_EDIT`       |
+| `/templates/{id}`      | delete a template                    | DELETE |                  | `TPL_DESTROY_ANY` OR `TPL_DESTROY` |
+| `/templates/{id}/disk` | download a template's disk           | GET    |                  | `TPL_READFS_ANY` OR `TPL_READFS`   |
 
 Remarks:
 
diff --git a/src/client/cmdTemplate/helper.go b/src/client/cmdTemplate/helper.go
index 6f3464813e076145e797a4f3776c4a58391e35b6..2fa149b2788f804c5b46382e4d230f4377f1cc24 100644
--- a/src/client/cmdTemplate/helper.go
+++ b/src/client/cmdTemplate/helper.go
@@ -141,7 +141,7 @@ func getFilteredTemplates(route string, patterns []string) ([]t.TemplateSerializ
     templatesList := []t.TemplateSerialized{}
 
     if resp.IsSuccess() {
-        templates, err := getTemplates(resp)
+        templates, err := deserializeTemplates(resp)
         if err != nil {
             return nil, err
         }
@@ -176,11 +176,20 @@ func getFilteredTemplates(route string, patterns []string) ([]t.TemplateSerializ
     }
 }
 
-// Retrieves all templates (no filtering).
-func getTemplates(resp *resty.Response) ([]t.TemplateSerialized, error) {
+// Deserialize a list of templates from an http response (no filtering).
+func deserializeTemplates(resp *resty.Response) ([]t.TemplateSerialized, error) {
     templates := []t.TemplateSerialized{}
     if err := json.Unmarshal(resp.Body(), &templates); err != nil {
         return nil, err
     }
     return templates, nil
-}
\ No newline at end of file
+}
+
+// Deserialize a single template from an http response.
+func deserializeTemplate(resp *resty.Response) (*t.TemplateSerialized, error) {
+    var tpl *t.TemplateSerialized = &t.TemplateSerialized{}
+    if err := json.Unmarshal(resp.Body(), &tpl); err != nil {
+        return tpl, err
+    }
+    return tpl, nil
+}
diff --git a/src/client/cmdVM/helper.go b/src/client/cmdVM/helper.go
index 306245a6b9d5bba6a11d4b85a5a9bcb7581888da..779c860c20b72e7e0de3471a6947dfbe51b8f549 100644
--- a/src/client/cmdVM/helper.go
+++ b/src/client/cmdVM/helper.go
@@ -134,7 +134,7 @@ func getFilteredVMs(route string, patterns []string) ([]vm.VMNetworkSerialized,
     vmsList := []vm.VMNetworkSerialized{}
 
     if resp.IsSuccess() {
-        vms, err := getVMs(resp)
+        vms, err := deserializeVMs(resp)
         if err != nil {
             return nil, err
         }
@@ -169,8 +169,8 @@ func getFilteredVMs(route string, patterns []string) ([]vm.VMNetworkSerialized,
     }
 }
 
-// Retrieves all VMs (no filtering).
-func getVMs(resp *resty.Response) ([]vm.VMNetworkSerialized, error) {
+// Deserialize a list of VMs from an http response (no filtering).
+func deserializeVMs(resp *resty.Response) ([]vm.VMNetworkSerialized, error) {
     vms := []vm.VMNetworkSerialized{}
     if err := json.Unmarshal(resp.Body(), &vms); err != nil {
         return nil, err
@@ -178,8 +178,8 @@ func getVMs(resp *resty.Response) ([]vm.VMNetworkSerialized, error) {
     return vms, nil
 }
 
-// Retrieve a single VM.
-func getVM(resp *resty.Response) (*vm.VMNetworkSerialized, error) {
+// Deserialize a single VM from an http response.
+func deserializeVM(resp *resty.Response) (*vm.VMNetworkSerialized, error) {
     var vm *vm.VMNetworkSerialized = &vm.VMNetworkSerialized{}
     if err := json.Unmarshal(resp.Body(), vm); err != nil {
         return vm, err
diff --git a/src/client/cmdVM/vmCreate.go b/src/client/cmdVM/vmCreate.go
index a034f94a1b0134a83ec293178b324d79d6ec32d1..55c29ba30150361683ce7f0b24920d050aca3433 100644
--- a/src/client/cmdVM/vmCreate.go
+++ b/src/client/cmdVM/vmCreate.go
@@ -138,7 +138,7 @@ func (cmd *Create)Run(args []string) int {
             statusCode = 1
         } else {
             if resp.IsSuccess() {
-                vm, err := getVM(resp)
+                vm, err := deserializeVM(resp)
                 if err != nil {
                     u.PrintlnErr("Failed retrieving server's response: "+err.Error())
                     statusCode = 1
diff --git a/src/client/cmdVM/vmListSingle.go b/src/client/cmdVM/vmListSingle.go
index aa032c1772abcd31bfde684bf1303b023d3b5115..2c970ebd6ebed191046b76f59fd61f1f81eb8e4a 100644
--- a/src/client/cmdVM/vmListSingle.go
+++ b/src/client/cmdVM/vmListSingle.go
@@ -45,7 +45,7 @@ func (cmd *ListSingle)Run(args []string) int {
         return 1
     } else {
         if resp.IsSuccess() {
-            vm, err := getVM(resp)
+            vm, err := deserializeVM(resp)
             if err != nil {
                 u.PrintlnErr("Failed retrieving server's response: "+err.Error())
                 return 1
diff --git a/src/client/nexush/nexush.go b/src/client/nexush/nexush.go
index 5787b35be8514d615c47458c655db3f272f9f859..a6bb3915c36906126c3881f55bc9c8659bb8d2dc 100644
--- a/src/client/nexush/nexush.go
+++ b/src/client/nexush/nexush.go
@@ -49,6 +49,7 @@ var cmdList = []cmd.Command {
     &cmdTemplate.Edit{"tpledit"},
     &cmdTemplate.ExportDisk{"tplexportdisk"},
     &cmdTemplate.List{"tpllist"},
+    // &cmdTemplate.ListSingle{"tpllistsingle"},   // for testing the route only
 
     &cmdMisc.HelpHeader{"!═════╡ VM commands ╞═════════════════════════════════════════════════════════════════"},
     &cmdVM.AddAccess{"vmaddaccess"},
@@ -63,7 +64,7 @@ var cmdList = []cmd.Command {
     &cmdVM.ImportDir{"vmimportdir"},
     &cmdVM.Stop{"vmkill"},
     &cmdVM.List{"vmlist"},
-    &cmdVM.ListSingle{"vmlistsingle"},
+    // &cmdVM.ListSingle{"vmlistsingle"},   // for testing the route only
     &cmdVM.Reboot{"vmreboot"},
     &cmdVM.Shutdown{"vmshutdown"},
     &cmdVM.Start{"vmstart"},
diff --git a/src/server/router/router.go b/src/server/router/router.go
index e35b910db1d83ca56ff73a0561138d8b2c9afe89..103baca741645301287503cc76971b5e4c616cc4 100644
--- a/src/server/router/router.go
+++ b/src/server/router/router.go
@@ -99,6 +99,7 @@ func (router *Router)Start(port int) {
     // Template management.
     templatesGroup := router.echo.Group("/templates")
     templatesGroup.Use(middleware.JWTWithConfig(auth.GetTokenAccess()))
+    templatesGroup.GET("/:id", router.tpl.GetTemplate)
     templatesGroup.GET("", router.tpl.GetTemplates)
     templatesGroup.POST("/vm", router.tpl.CreateTemplateFromVM)
     templatesGroup.POST("/qcow", router.tpl.CreateTemplateFromQCOW)
diff --git a/src/server/router/routerTemplates.go b/src/server/router/routerTemplates.go
index 6c7f51fdb08128c86e7cd2a9ea5b981a5242176c..ebeacd8348942bd17012c12a788a6002906c31d1 100644
--- a/src/server/router/routerTemplates.go
+++ b/src/server/router/routerTemplates.go
@@ -52,6 +52,41 @@ func (r *RouterTemplates)GetTemplates(c echo.Context) error {
     }
 }
 
+// Returns a template that can be listed based on its UUID.
+// Requires either capability:
+// CAP_TPL_LIST_ANY: any templates can be listed.
+// CAP_TPL_LIST: only templates owned by the user as well as public templates can be listed.
+// curl --cacert ca.pem -X GET https://localhost:1077/templates/62ae8791-c108-4235-a7d6-074e9b6a9017 -H "Authorization: Bearer <AccessToken>"
+func (r *RouterTemplates)GetTemplate(c echo.Context) error {
+    // Retrieves logged user from context.
+    user, err := getLoggedUser(r.users, c)
+    if err != nil {
+        return echo.NewHTTPError(http.StatusUnauthorized, err.Error())
+    }
+
+    // Retrieves the template based on its UUID.
+    id, err := uuid.Parse(c.Param("id"))
+    if err != nil {
+        return echo.NewHTTPError(http.StatusBadRequest, err.Error())
+    }
+    tpl, err := r.tpl.GetTemplate(id)
+    if err != nil {
+        return echo.NewHTTPError(http.StatusNotFound, err.Error())
+    }
+
+    // If user has CAP_TPL_LIST_ANY, returns the template.
+    if user.HasCapability(caps.CAP_TPL_LIST_ANY) {
+        return c.JSONPretty(http.StatusOK, tpl.SerializeToNetwork(), "    ")
+    } else if user.HasCapability(caps.CAP_TPL_LIST) {
+        // Returns template if owned by the user or template is public.
+        if tpl.GetOwner() == user.Email || tpl.IsPublic() {
+            return c.JSONPretty(http.StatusOK, tpl.SerializeToNetwork(), "    ")
+        }
+    }
+
+    return echo.NewHTTPError(http.StatusUnauthorized, msgInsufficientCaps)
+}
+
 // Creates a template from a VM.
 // Requires CAP_TPL_CREATE.
 // curl --cacert ca.pem -X POST https://localhost:1077/templates/vm -H 'Content-Type: application/json' -d '{"vmID":"e41f3556-ca24-4658-bd79-8c85bd6bff59","name":"Xubuntu_22.04","access":"public"}' -H "Authorization: Bearer <AccessToken>"
diff --git a/src/server/router/routerVMs.go b/src/server/router/routerVMs.go
index d687db857ce89e1f16e06fc8d35f86f2b957ec10..2d2887633046c322ffc88fc5917cfa13824417a0 100644
--- a/src/server/router/routerVMs.go
+++ b/src/server/router/routerVMs.go
@@ -3,7 +3,6 @@ package router
 import (
     "io"
     "os"
-    // "errors"
     "net/http"
     "path/filepath"
     "nexus-common/caps"
@@ -44,8 +43,14 @@ func (r *RouterVMs)GetListableVMs(c echo.Context) error {
 // Requires to be the VM's owner, or either capability:
 // User cap: CAP_VM_LIST_ANY: returns the VM.
 // VM access cap: CAP_VM_LIST: returns the VM with this cap for the logged user.
-// curl --cacert ca.pem -X GET https://localhost:1077/vm/62ae8791-c108-4235-a7d6-074e9b6a9017 -H "Authorization: Bearer <AccessToken>"
+// curl --cacert ca.pem -X GET https://localhost:1077/vms/62ae8791-c108-4235-a7d6-074e9b6a9017 -H "Authorization: Bearer <AccessToken>"
 func (r *RouterVMs)GetListableVM(c echo.Context) error {
+    // Retrieves logged user from context.
+    user, err := getLoggedUser(r.users, c)
+    if err != nil {
+        return echo.NewHTTPError(http.StatusUnauthorized, err.Error())
+    }
+
     // Retrieves the VM based on its UUID.
     id, err := uuid.Parse(c.Param("id"))
     if err != nil {
@@ -56,12 +61,6 @@ func (r *RouterVMs)GetListableVM(c echo.Context) error {
         return echo.NewHTTPError(http.StatusNotFound, err.Error())
     }
 
-    // Retrieves logged user from context.
-    user, err := getLoggedUser(r.users, c)
-    if err != nil {
-        return echo.NewHTTPError(http.StatusUnauthorized, err.Error())
-    }
-
     // If user has CAP_VM_LIST_ANY capability, returns the VM.
     if user.HasCapability(caps.CAP_VM_LIST_ANY) {
         return c.JSONPretty(http.StatusOK, vm.SerializeToNetwork(), "    ")