diff --git a/composer.json b/composer.json
index 4aa66c5..f57d8e7 100644
--- a/composer.json
+++ b/composer.json
@@ -38,7 +38,8 @@
         "lean" : {
             "autoload": {
                 "definitions" : [
-                    "meta/definitions/software.php"
+                    "meta/definitions/software.php",
+                    "meta/definitions/storage.php",
                 ],
                 "config": [
                     "meta/config.php"
diff --git a/meta/config.php b/meta/config.php
index d46d5fc..a723c71 100644
--- a/meta/config.php
+++ b/meta/config.php
@@ -1,9 +1,28 @@
 <?php
 
+$path = dirname(__DIR__, 1);
+
 return [
     'lean' => [
         'autoload' => [
             'lean.api',
         ]
     ],
-];
+
+    'ulmus' => [
+        'connections' => [
+            'lean.api' => [
+                'adapter'  => getenv("LEAN_API_ADAPTER") ?: "SQLite",
+                'path'     =>  getenv('PROJECT_PATH') . DIRECTORY_SEPARATOR . ( getenv("LEAN_API_PATH") ?: "var/lean-api.sqlite3" ),
+                'pragma_begin' => array_merge(
+                    explode(',', getenv("LEAN_API_PRAGMA_BEGIN") ?: "foreign_keys=ON,synchronous=NORMAL"),
+                    explode(',', getenv('DEBUG') ? getenv("LEAN_API_PRAGMA_DEBUG_BEGIN") : "journal_mode=WAL")
+                ),
+                'pragma_close' => array_merge(
+                    explode(',', getenv("LEAN_API_PRAGMA_CLOSE") ?: "analysis_limit=500,optimize"),
+                    explode(',', getenv('DEBUG') ? getenv("LEAN_API_PRAGMA_DEBUG_CLOSE") : "")
+                ),
+            ],
+        ]
+    ]
+];
\ No newline at end of file
diff --git a/meta/definitions/software.php b/meta/definitions/software.php
index f90df3d..497042d 100644
--- a/meta/definitions/software.php
+++ b/meta/definitions/software.php
@@ -1,6 +1,8 @@
 <?php
 
-$path = dirname(__DIR__, 2);
+use Ulmus\ConnectionAdapter;
+
+putenv('LEAN_API_PROJECT_PATH=' . $path = dirname(__DIR__, 2));
 
 return [
     'lean.api' => [
@@ -22,6 +24,13 @@ return [
             ],
         ],
 
+        'ulmus' => [
+            'entities' => [ 'Lean\\Api\\Entity' => implode(DIRECTORY_SEPARATOR, [ $path, 'src', 'Entity', '' ]) ],
+            'adapters' => [
+                DI\get('lean.api:storage'),
+            ]
+        ],
+
         'tell' => [
             'json' => [
                 [
@@ -31,10 +40,19 @@ return [
             ]
         ],
 
-        /*'routes' => [
+        'routes' => [
             'Lean\\Api\\Controller' => implode(DIRECTORY_SEPARATOR, [ $path, "src", "Controller", "" ]),
-        ],*/
+        ],
     ],
 
+    'lean.api:storage' => function($c) {
+        $adapter = new ConnectionAdapter('lean.api', $c->get('config')['ulmus'], false);
+        $adapter->resolveConfiguration();
+
+        return $adapter;
+    },
+
     \Lean\Api\Factory\MessageFactoryInterface::class => DI\autowire(\Lean\Api\Lib\Message::class),
+    \League\Route\Strategy\ApplicationStrategy::class => DI\autowire(\Lean\Api\ApplicationStrategy::class),
+    \Lean\Api\Factory\DebugFormFactoryInterface::class => DI\autowire(\Lean\Api\Factory\DebugFormFactory::class),
 ];
\ No newline at end of file
diff --git a/meta/definitions/storage.php b/meta/definitions/storage.php
new file mode 100644
index 0000000..59ef76c
--- /dev/null
+++ b/meta/definitions/storage.php
@@ -0,0 +1,19 @@
+<?php
+
+use Psr\Container\ContainerInterface;
+
+use Ulmus\ConnectionAdapter,
+    Ulmus\Container\AdapterProxy;
+
+use Storage\Session;
+
+use function DI\autowire, DI\create, DI\get;
+
+return [
+   'lean.api:storage' => function($c) {
+        $adapter = new ConnectionAdapter('lean.api', $c->get('config')['ulmus'], false);
+        $adapter->resolveConfiguration();
+
+        return $adapter;
+    },
+];
diff --git a/meta/docs/debug.md b/meta/docs/debug.md
new file mode 100644
index 0000000..f127101
--- /dev/null
+++ b/meta/docs/debug.md
@@ -0,0 +1,21 @@
+# LEAN API
+
+## Debug tooling to help development within Lean's API
+
+[Documentation](../) / Debug API queries
+
+--------------------------------------------------------------------------------
+
+### Routes
+
+{route:descriptor}
+
+{request:debugger}
+
+### Forms / actions
+
+{form:descriptor}
+
+### Entities
+
+{entity:descriptor}
\ No newline at end of file
diff --git a/meta/docs/debug/errors.md b/meta/docs/debug/errors.md
new file mode 100644
index 0000000..9d2f81a
--- /dev/null
+++ b/meta/docs/debug/errors.md
@@ -0,0 +1,21 @@
+# LEAN API
+
+## Debug tooling to help development within Lean's API
+
+[Lean API](../../debug) / Debug API queries
+
+--------------------------------------------------------------------------------
+
+### Routes
+
+{route:descriptor}
+
+{request:debugger}
+
+### Forms / actions
+
+{form:descriptor}
+
+### Entities
+
+{entity:descriptor}
\ No newline at end of file
diff --git a/meta/docs/lean-api.md b/meta/docs/lean-api.md
new file mode 100644
index 0000000..6877c23
--- /dev/null
+++ b/meta/docs/lean-api.md
@@ -0,0 +1,7 @@
+# Lean API
+
+## Lean's API internal debug API
+
+### Routes disponibles
+
+- [Debug / Errors](./debug/errors/documentation)
diff --git a/meta/i18n/fr/lean.api.json b/meta/i18n/fr/lean.api.json
index 857bd49..01ebd76 100644
--- a/meta/i18n/fr/lean.api.json
+++ b/meta/i18n/fr/lean.api.json
@@ -12,11 +12,11 @@
   },
   "form": {
     "error": {
-      "entity": "Une propriété $entity est nécessaire dans ce form"
+      "entity": "Une propriété $entity est nécessaire pour envoyer ce formulaire."
     },
     "save": {
       "error": {
-        "save": "Une erreur est survenue en tenant de sauvegarder les données",
+        "entity": "Une erreur est survenue en tenant de sauvegarder les données pour l'entité {$entity}.",
         "pdo": "Une erreur est survenue : '{$error}'"
       },
       "success": {
diff --git a/src/ApplicationStrategy.php b/src/ApplicationStrategy.php
new file mode 100644
index 0000000..33aeca1
--- /dev/null
+++ b/src/ApplicationStrategy.php
@@ -0,0 +1,72 @@
+<?php
+
+namespace Lean\Api;
+
+use League\Route\Strategy;
+
+use League\Route\Http\Exception\NotFoundException;
+use Lean\Factory\HttpFactoryInterface;
+use Picea\Asset\Asset;
+use Psr\Container\ContainerInterface;
+use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\ServerRequestInterface;
+use Psr\Http\Server\MiddlewareInterface;
+use Picea\Picea;
+use Psr\Http\Server\RequestHandlerInterface;
+use function DI\get;
+
+class ApplicationStrategy extends Strategy\ApplicationStrategy {
+
+    public function __construct(
+        protected HttpFactoryInterface $httpFactory,
+        ContainerInterface $di,
+    ) {
+        $this->container = $di;
+    }
+
+    public function getThrowableHandler(): MiddlewareInterface
+    {
+        return new class implements MiddlewareInterface
+        {
+            public function process(
+                ServerRequestInterface $request,
+                RequestHandlerInterface $handler
+            ): ResponseInterface {
+                try {
+                    return $handler->handle($request);
+                } catch (\Throwable $ex) {
+                    echo sprintf("%s in <strong>%s</strong> <pre>%s</pre>", $ex->getMessage(), $ex->getFile() . ":" . $ex->getLine(), $ex->getTraceAsString());
+                    exit();
+                }
+            }
+        };
+    }
+
+    public function getNotFoundDecorator(NotFoundException $exception): MiddlewareInterface
+    {
+        return new class($this->httpFactory) implements MiddlewareInterface {
+
+            public function __construct(
+                protected HttpFactoryInterface $httpFactory,
+            ) {}
+
+            public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
+            {
+                if (php_sapi_name() !== 'cli' and ! defined('STDIN'))  {
+                    return $this->throw404($request);
+                }
+
+                return $handler->handle($request);
+            }
+
+            public function throw404(ServerRequestInterface $request) : ResponseInterface
+            {
+                return $this->httpFactory->createJsonResponse([
+                    'status' => 'error',
+                    'message' => "Not Found",
+                    'ts' => time(),
+                ], 404);
+            }
+        };
+    }
+}
\ No newline at end of file
diff --git a/src/Attribute/ContextField.php b/src/Attribute/ContextField.php
index 0b04e15..1e67e25 100644
--- a/src/Attribute/ContextField.php
+++ b/src/Attribute/ContextField.php
@@ -12,5 +12,6 @@ class ContextField
         public ?int $maxLength = null,
         public ?string $regexFormat = null,
         public ?string $example = null,
+        public bool $hidden = false,
     ) {}
 }
\ No newline at end of file
diff --git a/src/Controller/Debug.php b/src/Controller/Debug.php
new file mode 100644
index 0000000..8b2c538
--- /dev/null
+++ b/src/Controller/Debug.php
@@ -0,0 +1,23 @@
+<?php
+
+namespace Lean\Api\Controller;
+
+use Lean\Api\{Controller, Entity, Form, Lib};
+use Notes\Route\Attribute\Method\Route;
+use Notes\Route\Attribute\Object\Route as RouteObj;
+use Notes\Security\Attribute\Security;
+use Notes\Security\Attribute\Taxus;
+use Psr\Http\Message\{ResponseInterface, ServerRequestInterface};
+
+#[Security(locked: false)]
+#[RouteObj(base: "/lean/debug")]
+class Debug {
+
+    use \Lean\Api\Lib\ControllerTrait;
+
+    #[Route("/", name: "lean.api:debug-doc", method: "GET", description: "Documentation section")]
+    public function documentation(ServerRequestInterface $request, array $arguments) : ResponseInterface
+    {
+        return $this->renderMarkdown(getenv("LEAN_API_PROJECT_PATH") . "/meta/docs/lean-api.md", [ Entity\Error::class, ], [ Form\Debug\ErrorContext::class ]);
+    }
+}
diff --git a/src/Controller/Debug/Errors.php b/src/Controller/Debug/Errors.php
new file mode 100644
index 0000000..75e6a7c
--- /dev/null
+++ b/src/Controller/Debug/Errors.php
@@ -0,0 +1,46 @@
+<?php
+
+namespace Lean\Api\Controller\Debug;
+
+use Lean\Api\{Controller, Entity, Form, Lib};
+use Notes\Route\Attribute\Method\Route;
+use Notes\Route\Attribute\Object\Route as RouteObj;
+use Notes\Security\Attribute\Security;
+use Notes\Security\Attribute\Taxus;
+use Psr\Http\Message\{ResponseInterface, ServerRequestInterface};
+
+#[Security(locked: false)]
+## [Taxus(Entity\UserTypeEnum::Developer)]
+#[RouteObj(base: "/lean/debug/errors")]
+class Errors {
+
+    use \Lean\Api\Lib\ControllerTrait;
+
+    #[Route("/documentation", name: "lean.api:errors-doc", method: "GET", description: "Documentation section")]
+    public function documentation(ServerRequestInterface $request, array $arguments) : ResponseInterface
+    {
+        return $this->renderMarkdown(getenv("LEAN_API_PROJECT_PATH") . "/meta/docs/debug/errors.md", [ Entity\Error::class, ], [ Form\Debug\ErrorContext::class ]);
+    }
+
+    #[Route("/", name: "lean.api:errors-list", method: [ "GET" ], description: "List every errors received")]
+    public function list(ServerRequestInterface $request, array $arguments): ResponseInterface
+    {
+        $search = Entity\Error::searchRequest()->fromRequest($request);
+        $collection = Entity\Error::repository()->filterServerRequest($search)->loadAll()->iterate(function($e) {
+            unset($e->trace);
+        });
+
+        return $this->output($collection, $search->count);
+    }
+
+    #[Route("/{id:\d+}", name: "lean.api:errors-single", method: [ "GET" ], description: "Get a single error from it's ID")]
+    public function single(ServerRequestInterface $request, array $arguments): ResponseInterface
+    {
+        $type = Entity\Error::class;
+        $request = $this->searchEntityFromRequest($request, $type);
+        $result = $request->getAttribute('lean.searchRequest');
+
+        return $this->output($result[$type]->entity, $result[$type]->search->count);
+    }
+
+}
diff --git a/src/Entity/Error.php b/src/Entity/Error.php
new file mode 100644
index 0000000..2dc2468
--- /dev/null
+++ b/src/Entity/Error.php
@@ -0,0 +1,79 @@
+<?php
+
+namespace Lean\Api\Entity;
+
+use Lean\Api\Attribute\EntityField;
+use Ulmus\EntityTrait,
+    Ulmus\SearchRequest\SearchableInterface,
+    Ulmus\SearchRequest\SearchRequestInterface,
+    Ulmus\Entity\Field\Datetime;
+
+use Ulmus\{Attribute\Obj\Table,
+    Entity\EntityInterface};
+
+use Ulmus\{Api\Common\MethodEnum,
+    SearchRequest,
+    SearchRequest\Attribute\PropertyValueModifier\Split,
+    SearchRequest\Attribute\PropertyValueSource,
+    SearchRequest\Attribute\SearchOrderBy,
+    SearchRequest\Attribute\SearchWhere,
+    SearchRequest\SearchMethodEnum};
+
+use Ulmus\Attribute\Property\{Field, Filter, FilterJoin, Relation, Join, Virtual, Where};
+
+#[Table(name: "debug_errors", adapter: "lean.api")]
+class Error implements EntityInterface, \JsonSerializable {
+    use EntityTrait;
+
+    #[Field\Id]
+    public int $id;
+
+    #[Field]
+    #[EntityField(description: "URL from which the error occurred")]
+    public string $url;
+
+    #[Field]
+    #[EntityField(description: "HTTP method", field: "httpMethod")]
+    public MethodEnum $method;
+
+    #[Field]
+    #[EntityField(description: "Error message")]
+    public string $message;
+
+    #[Field]
+    #[EntityField(description: "Error code")]
+    public int $code;
+
+    #[Field]
+    #[EntityField(description: "File from which the error occurred")]
+    public string $file;
+
+    #[Field]
+    #[EntityField(description: "Line in the file from which the error occurred")]
+    public int $line;
+
+    #[Field]
+    #[EntityField(description: "Stack trace of the exception")]
+    public array $trace;
+
+    #[Field\CreatedAt(name: "created_at")]
+    public Datetime $createdAt;
+
+    public function __toString() {
+        return mb_strlen($this->message) > 255 ? mb_substr($this->message, 0, 255) . "[...]" : $this->message;
+    }
+
+    public static function searchRequest(...$arguments) : SearchRequestInterface
+    {
+        return new #[SearchRequest\Attribute\SearchRequestParameter(Error::class)] class(...$arguments) extends SearchRequest\SearchRequest {
+            #[SearchRequest\Attribute\SearchWhere(source: SearchRequest\Attribute\PropertyValueSource::RequestAttribute)]
+            public int $id;
+
+            #[SearchWhere(method: SearchMethodEnum::Like)]
+            public string $message;
+
+            #[SearchOrderBy(parameter: "created_at",)]
+            public string $createdAt = SearchOrderBy::DESCENDING;
+        };
+    }
+}
\ No newline at end of file
diff --git a/src/Entity/User/UserPrivilegeInterface.php b/src/Entity/User/UserPrivilegeInterface.php
new file mode 100644
index 0000000..6cf34a2
--- /dev/null
+++ b/src/Entity/User/UserPrivilegeInterface.php
@@ -0,0 +1,11 @@
+<?php
+
+namespace Lean\Api\Entity\User;
+
+interface UserPrivilegeInterface
+{
+    public function isDeveloper() : bool;
+    public function isAdmin() : bool;
+    public function isUser() : bool;
+    public function isAnonymous() : bool;
+}
\ No newline at end of file
diff --git a/src/Factory/DebugFormFactory.php b/src/Factory/DebugFormFactory.php
new file mode 100644
index 0000000..463b753
--- /dev/null
+++ b/src/Factory/DebugFormFactory.php
@@ -0,0 +1,39 @@
+<?php
+
+namespace Lean\Api\Factory;
+
+use Lean\Api\{Factory, Form, Entity, Lib};
+use Lean\LanguageHandler;
+use Picea\Ui\Method\FormContextInterface;
+use Picea\Ui\Method\FormInterface;
+use Psr\Http\Message\ServerRequestInterface;
+use Ulmus\Entity\EntityInterface;
+
+class DebugFormFactory implements DebugFormFactoryInterface
+{
+    public function __construct(
+        protected ServerRequestInterface $request,
+        protected LanguageHandler $languageHandler,
+        protected MessageFactoryInterface $messageFactory,
+    ) {}
+
+    public function errorSave(EntityInterface|Entity\Error $entity): FormInterface
+    {
+        return new Form\Debug\ErrorSave($this->languageHandler, $this->messageFactory, $entity);
+    }
+
+    public function errorSaveContext(? ServerRequestInterface $request = null, ? string $formName = null): FormContextInterface
+    {
+        return new Form\Debug\ErrorContext($request ?: $this->request, $formName);
+    }
+
+    public function errorDelete(EntityInterface|Entity\Error $entity): FormInterface
+    {
+        return new Form\Debug\ErrorDelete($this->languageHandler, $this->messageFactory, $entity);
+    }
+
+    public function errorDeleteContext(?ServerRequestInterface $request = null, ?string $formName = null): FormContextInterface
+    {
+        return new Lib\FormContext($request, $formName);
+    }
+}
\ No newline at end of file
diff --git a/src/Factory/DebugFormFactoryInterface.php b/src/Factory/DebugFormFactoryInterface.php
new file mode 100644
index 0000000..4ac1936
--- /dev/null
+++ b/src/Factory/DebugFormFactoryInterface.php
@@ -0,0 +1,21 @@
+<?php
+
+namespace Lean\Api\Factory;
+
+use Picea\Ui\Method\FormContextInterface;
+use Picea\Ui\Method\FormInterface;
+use Psr\Http\Message\ServerRequestInterface;
+use Lean\LanguageHandler;
+use Ulmus\Entity\EntityInterface;
+use Lean\Api\Entity;
+
+interface DebugFormFactoryInterface
+{
+    public function __construct(ServerRequestInterface $request, LanguageHandler $languageHandler, MessageFactoryInterface $messageFactory,);
+
+    ### ERROR SAVE
+    public function errorSave(Entity\Error|EntityInterface $entity): FormInterface;
+    public function errorSaveContext(?ServerRequestInterface $request = null, ? string $formName = null): FormContextInterface;
+    public function errorDelete(Entity\Error|EntityInterface $entity): FormInterface;
+    public function errorDeleteContext(?ServerRequestInterface $request = null, ? string $formName = null): FormContextInterface;
+}
\ No newline at end of file
diff --git a/src/Form/Debug/ErrorContext.php b/src/Form/Debug/ErrorContext.php
new file mode 100644
index 0000000..80f5f49
--- /dev/null
+++ b/src/Form/Debug/ErrorContext.php
@@ -0,0 +1,45 @@
+<?php
+
+namespace Lean\Api\Form\Debug;
+
+use Lean\Api\{Entity};
+use Lean\Api\Attribute\ContextField;
+use Picea\Ui\Method\FormContext;
+use Ulmus\Api\Common\MethodEnum;
+use Ulmus\Entity\Field\Datetime;
+
+#[ContextField(description: "This form saves an error received by the API")]
+class ErrorContext extends FormContext
+{
+    #[ContextField(description: "URL which the error was received from", hidden: true, )]
+    public string $url;
+
+    #[ContextField( description: "HTTP method used when this error was generated", hidden: true, )]
+    public MethodEnum|string $httpMethod;
+
+    #[ContextField(description: "Error message generated")]
+    public string $message;
+
+    #[ContextField(description: "HTTP error code")]
+    public int $code;
+
+    #[ContextField(description: "File in which the error occured")]
+    public string $file;
+
+    #[ContextField(description: "Line of the error")]
+    public int $line;
+
+    #[ContextField(description: "Complete stack trace (when available)")]
+    public array $trace;
+
+    public function valid(? Entity\Error $error = null): bool
+    {
+        $server = $this->getRequest()->getServerParams();
+
+        $this->url = ( ( 'on' === ( $server['HTTPS'] ?? false ) ) ? 'https' : 'http' ) . '://' . $server['HTTP_HOST'] . $server["REQUEST_URI"];
+
+        $this->httpMethod = MethodEnum::from($server['REQUEST_METHOD']);
+
+        return parent::valid();
+    }
+}
\ No newline at end of file
diff --git a/src/Form/Debug/ErrorSave.php b/src/Form/Debug/ErrorSave.php
new file mode 100644
index 0000000..03e937c
--- /dev/null
+++ b/src/Form/Debug/ErrorSave.php
@@ -0,0 +1,20 @@
+<?php
+
+namespace Lean\Api\Form\Debug;
+
+use Lean\Api\Factory\MessageFactoryInterface;
+use Lean\LanguageHandler;
+use Lean\Api\{Lib, Entity};
+
+class ErrorSave extends \Lean\Api\Form\Save {
+
+    public function __construct(
+        protected LanguageHandler $languageHandler,
+        protected MessageFactoryInterface $message,
+        public Entity\Error $entity,
+    ) {
+        parent::__construct($this->languageHandler, $this->message);
+
+        $this->contextClass = ErrorContext::class;
+    }
+}
diff --git a/src/Form/Save.php b/src/Form/Save.php
index 3bd9eaa..67794d8 100644
--- a/src/Form/Save.php
+++ b/src/Form/Save.php
@@ -22,17 +22,17 @@ abstract class Save implements \Picea\Ui\Method\FormInterface {
         return $context->valid($this->getEntity()->isLoaded() ? $this->getEntity() : null);
     }
 
-    public function execute(FormContextInterface $context) : void
+    public function execute(FormContextInterface $context) : mixed
     {
         $entity = $this->getEntity();
 
-        if  ($entity->isLoaded() ) {
-            if (property_exists($entity, 'updatedAt')) {
+        if  ($entity->isLoaded()) {
+            if (property_exists($entity, 'updatedAt') && $entity->repository()->generateDatasetDiff($entity) ) {
                 $entity->updatedAt = new Datetime();
             }
         }
         else {
-            if (property_exists($entity, 'createdAt')) {
+            if (property_exists($entity, 'createdAt') && empty($entity->createdAt)) {
                 $entity->createdAt = new Datetime();
             }
         }
@@ -40,18 +40,22 @@ abstract class Save implements \Picea\Ui\Method\FormInterface {
         try {
             $this->assignContextToEntity($context);
 
-            if ( $entity::repository()->save($entity) ) {
+            if ( $saved = $entity::repository()->save($entity) ) {
                 $context->pushMessage($this->message::generateSuccess(
                     $this->lang('lean.api.form.save.success.entity')
                 ));
             }
             else {
-                throw new \InvalidArgumentException($this->lang('lean.api.form.save.error.entity'));
+                $context->pushMessage($this->message::generateWarning(
+                    $this->lang('lean.api.form.save.error.entity', [ 'entity' => $this->entity::class ])
+                ));
             }
         }
         catch(\Throwable $ex) {
-            throw new \ErrorException($this->lang('lean.api.form.save.error.pdo', [ 'error' => $ex->getMessage() ]));
+            throw new \PDOException($this->lang('lean.api.form.save.error.pdo', [ 'error' => $ex->getMessage() ]));
         }
+
+        return $saved;
     }
 
     protected function assignContextToEntity(FormContextInterface $context) : void
@@ -60,13 +64,12 @@ abstract class Save implements \Picea\Ui\Method\FormInterface {
 
         foreach($entity::resolveEntity()->fieldList() as $key => $property) {
             $field = $property->getAttribute(Field::class)->object;
-
             if (! $field->readonly || ! $entity->isLoaded()) {
                 $apiField = $property->getAttribute(EntityField::class)->object ?? null;
-
                 if ($apiField) {
                     $var = $apiField->field ?: $key;
 
+
                     if ( isset($context->{$var}) ) {
                         if ($apiField->setterMethod) {
                             # Use a setter method
diff --git a/src/FormDescriptor.php b/src/FormDescriptor.php
index b2306b1..fad6fda 100644
--- a/src/FormDescriptor.php
+++ b/src/FormDescriptor.php
@@ -15,7 +15,6 @@ class FormDescriptor
 
         foreach($forms as $form) {
             $fields = [];
-            $propertyHtml = "";
             $formName = is_object($form) ? $form::class : $form;
             $reflector = new ObjectReflection($formName);
 
@@ -24,17 +23,19 @@ class FormDescriptor
             foreach($reflector->reflectProperties() as $property) {
                 $field = $property->getAttribute(ContextField::class);
 
-                if ($field) {
+                if ($field && ! $field->object->hidden ) {
                     $types = $property->getTypes();
+
                     $fields[] = [
                         'name' => $property->name,
                         'description' => $field->object->description,
-                        'type' => $field->object->type ?? implode(' | ', array_map(fn($e) => $e->type, $types)),
+                        'type' => $field->object->type ?? $types,
                         'allowNulls' => $property->allowsNull(),
-                        'regexPattern' =>$field->object->regexFormat ?? "aucun",
-                        'minLength' =>$field->object->minLength ?? "aucune",
-                        'maxLength' =>$field->object->maxLength ?? "aucune",
-                        'example' =>$field->object->example,
+                        'regexPattern' => $field->object->regexFormat ?? null,
+                        'minLength' => $field->object->minLength ?? null,
+                        'maxLength' => $field->object->maxLength ?? null,
+                        'example' =>  $field->object->example ?? null,
+                        'values' => $this->generateExampleValues($types),
                     ];
                 }
             }
@@ -48,4 +49,17 @@ class FormDescriptor
 
         return $list;
     }
+
+    protected function generateExampleValues(array $types) : string|array
+    {
+        $values = [];
+
+        foreach($types as $type) {
+            if ( enum_exists($type->type) ) {
+                $values = array_merge($values, array_map(fn($e) => $e->value, $type->type::cases()));
+            }
+        }
+
+        return $values;
+    }
 }
\ No newline at end of file
diff --git a/src/LeanApiTrait.php b/src/LeanApiTrait.php
index 0bf08bb..c0f5100 100644
--- a/src/LeanApiTrait.php
+++ b/src/LeanApiTrait.php
@@ -2,14 +2,24 @@
 
 namespace Lean\Api;
 
+use DI\Attribute\Inject;
 use League\CommonMark\CommonMarkConverter;
+use Lean\Api\Lib\ApiSearchRequestInterface;
 use Notes\Attribute\Ignore;
+use Psr\Container\ContainerInterface;
 use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\ServerRequestInterface;
+use Ulmus\Entity\EntityInterface;
+use Ulmus\EntityCollection;
+use Ulmus\SearchRequest\SearchRequestInterface;
 
 trait LeanApiTrait
 {
     use DescriptorTrait;
 
+    #[Inject]
+    protected ContainerInterface $container;
+
     #[Ignore]
     public function renderMarkdown(string $filepath, array $entities = [], array $forms = [], int $code = 200, array $headers = []) : ResponseInterface
     {
@@ -22,10 +32,15 @@ trait LeanApiTrait
         if (str_contains($markdown, '{route:descriptor}'))
         {
             $markdown = str_replace('{route:descriptor}', $this->renderRawView('lean-api/route_descriptor', [
-                'routes' => (new RouteDescriptor($this, $this->picea->compiler->getExtensionFromToken('url')))->getRoutes()
+                'routes' => (new RouteDescriptor($this, $this->picea->compiler->getExtensionFromToken('url'), $this->container))->getRoutes()
             ]), $markdown);
         }
 
+        if (str_contains($markdown, '{request:debugger}'))
+        {
+            $markdown = str_replace('{request:debugger}', $this->renderRawView('lean-api/request_debugger', []), $markdown);
+        }
+
         if (str_contains($markdown, '{entity:descriptor}'))
         {
             $markdown = str_replace('{entity:descriptor}', $this->renderRawView('lean-api/entity_descriptor', [
@@ -44,10 +59,81 @@ trait LeanApiTrait
     }
 
     #[Ignore]
-    protected function output(\JsonSerializable|array $data, int $count) {
+    protected function output(\JsonSerializable|array $data, int $count) : ResponseInterface
+    {
         return $this->renderJson([
             'data' => $data,
             'count' => $count,
         ]);
     }
+
+    #[Ignore]
+    protected function outputSearchResult(ApiSearchRequestInterface $apiSearchRequest) : ResponseInterface
+    {
+        return $this->output($apiSearchRequest->getResult(), $apiSearchRequest->getSearch()->count);
+    }
+
+    #[Ignore]
+        protected function searchEntityFromRequest(ServerRequestInterface $request, string $entityType, ? string $resultKey = null) : ServerRequestInterface
+    {
+        $search = $entityType::searchRequest()->fromRequest($request);
+        $entity = $entityType::repository()->filterServerRequest($search)->loadOne() ?? false;
+
+        if (! $entity ) {
+            throw new \InvalidArgumentException(sprintf("L'entré pour l'entité demandé (%s) est introuvable avec le ou les arguments fournis '%s'", $entityType, json_encode($request->getAttributes())));
+        }
+
+        return $request->withAttribute("lean.searchRequest",
+            [
+                $resultKey ?: $entityType => new class($search, $entity) implements ApiSearchRequestInterface {
+                    public function __construct(
+                        public readonly SearchRequestInterface $search,
+                        public readonly EntityInterface        $entity,
+                    ) {}
+
+                    public function getSearch(): SearchRequestInterface
+                    {
+                        return $this->search;
+                    }
+
+                    public function getResult(): EntityCollection|EntityInterface
+                    {
+                        return $this->entity;
+                    }
+                }
+            ] + $request->getAttribute("lean.searchRequest", [])
+        );
+    }
+
+    #[Ignore]
+    protected function searchEntitiesFromRequest(ServerRequestInterface $request, string $entityType, ? string $resultKey = null) : ServerRequestInterface
+    {
+        $search = $entityType::searchRequest()->fromRequest($request);
+        $entity = $entityType::repository()->filterServerRequest($search)->loadAll();
+
+        if (! $entity ) {
+            throw new \InvalidArgumentException(sprintf("L'entré pour l'entité demandé (%s) est introuvable avec le ou les arguments fournis '%s'", $entityType, json_encode($request->getAttributes())));
+        }
+
+        return $request->withAttribute("lean.searchRequest",
+            [
+                $resultKey ?: $entityType => new class($search, $entity) implements ApiSearchRequestInterface {
+                    public function __construct(
+                        public readonly SearchRequestInterface $search,
+                        public readonly EntityCollection       $collection,
+                    ) {}
+
+                    public function getSearch(): SearchRequestInterface
+                    {
+                        return $this->search;
+                    }
+
+                    public function getResult(): EntityCollection|EntityInterface
+                    {
+                        return $this->collection;
+                    }
+                }
+            ] + $request->getAttribute("lean.searchRequest", [])
+        );
+    }
 }
\ No newline at end of file
diff --git a/src/Lib/ApiSearchRequestInterface.php b/src/Lib/ApiSearchRequestInterface.php
new file mode 100644
index 0000000..07031f3
--- /dev/null
+++ b/src/Lib/ApiSearchRequestInterface.php
@@ -0,0 +1,13 @@
+<?php
+
+namespace Lean\Api\Lib;
+
+use Ulmus\Entity\EntityInterface;
+use Ulmus\EntityCollection;
+use Ulmus\SearchRequest\SearchRequestInterface;
+
+interface ApiSearchRequestInterface
+{
+    public function getSearch() : SearchRequestInterface;
+    public function getResult() : EntityCollection|EntityInterface;
+}
\ No newline at end of file
diff --git a/src/Lib/ControllerTrait.php b/src/Lib/ControllerTrait.php
new file mode 100644
index 0000000..b246fbe
--- /dev/null
+++ b/src/Lib/ControllerTrait.php
@@ -0,0 +1,13 @@
+<?php
+
+namespace Lean\Api\Lib;
+
+use Notes\Route\Attribute\Object\Route;
+
+#[Route(method: ['GET', 'POST', ])]
+trait ControllerTrait
+{
+    use \Lean\ControllerTrait, \Lean\Api\LeanApiTrait {
+        \Lean\Api\LeanApiTrait::renderMarkdown insteadof \Lean\ControllerTrait;
+    }
+}
\ No newline at end of file
diff --git a/src/Lib/FormContext.php b/src/Lib/FormContext.php
new file mode 100644
index 0000000..4c922e9
--- /dev/null
+++ b/src/Lib/FormContext.php
@@ -0,0 +1,5 @@
+<?php
+
+namespace Lean\Api\Lib;
+
+class FormContext extends \Picea\Ui\Method\FormContext { }
\ No newline at end of file
diff --git a/src/Middleware/ApiRenderer.php b/src/Middleware/ApiRenderer.php
index 2069e0c..0e74b5d 100644
--- a/src/Middleware/ApiRenderer.php
+++ b/src/Middleware/ApiRenderer.php
@@ -4,29 +4,44 @@ namespace Lean\Api\Middleware;
 
 use Laminas\Diactoros\Response\JsonResponse;
 use Lean\Factory\HttpFactory;
+use Picea\Ui\Method\FormHandler;
 use Psr\Http\Message\ResponseInterface;
 use Psr\Http\Message\ServerRequestInterface;
 use Psr\Http\Server\MiddlewareInterface;
 use Psr\Http\Server\RequestHandlerInterface;
+use Lean\Api\{Factory\DebugFormFactoryInterface, Form, Entity};
 
 class ApiRenderer implements MiddlewareInterface {
 
+    public function __construct(
+        protected DebugFormFactoryInterface $debugFormFactory,
+    ) {
+    }
+
     public function process(ServerRequestInterface $request, RequestHandlerInterface $handler) : ResponseInterface
     {
-        try { 
+        try {
             $response = $handler->handle($request);
         }
-        catch(\Exception $ex) {
-            if ( ! getenv('DEBUG') ) {
+        catch(\Throwable $ex) {
+            $errorId = $this->saveError($request, $ex)->id;
+
+            if ($this->awaitingJson($request)) {
                 return HttpFactory::createJsonResponse([
                     'status' => 'failed',
-                    'message' => $ex->getMessage(),
                     'ts' => time(),
-                ], 400);
-            }
-            else {
-                throw $ex;
+                    'data' => [
+                       # 'error_id' => $errorId,
+                        'message' => $ex->getMessage(),
+                        'file' => $ex->getFile(),
+                        'line' => $ex->getLine(),
+                        'code' => $ex->getCode(),
+                        'backtrace' => $ex->getTrace(),
+                    ]
+                ], 500);
             }
+
+            throw $ex;
         }
 
         if ($response instanceof JsonResponse) {
@@ -43,4 +58,30 @@ class ApiRenderer implements MiddlewareInterface {
 
         return $response;
     }
+
+    protected function saveError(ServerRequestInterface $request, \Throwable $exception) : Entity\Error
+    {
+        $request = $request->withMethod('PUT')->withParsedBody([]);
+
+        $context = $this->debugFormFactory->errorSaveContext($request);
+
+        $context->sets([
+            'message' => $exception->getMessage(),
+            'code' => $exception->getCode(),
+            'file' => $exception->getFile(),
+            'line' => $exception->getLine(),
+            'trace' => $exception->getTrace(),
+        ]);
+
+        $form = $this->debugFormFactory->errorSave($entity = new Entity\Error());
+
+        new FormHandler($request, $form, $context);
+
+        return $entity;
+    }
+
+    protected function awaitingJson(ServerRequestInterface $request) : bool
+    {
+        return str_contains(strtolower($request->getHeaderLine('content-type')), 'json');
+    }
 }
diff --git a/src/RouteDescriptor.php b/src/RouteDescriptor.php
index 2031254..ff0cd61 100644
--- a/src/RouteDescriptor.php
+++ b/src/RouteDescriptor.php
@@ -5,22 +5,27 @@ namespace Lean\Api;
 use Lean\Factory\HttpFactory;
 use Notes\ObjectReflection;
 use Notes\Route\Attribute\Method\Route;
+use Notes\Security\SecurityHandler;
 use Picea\Extension\UrlExtension;
+use Psr\Container\ContainerInterface;
+use Taxus\Taxus;
 
 class RouteDescriptor
 {
-    public string $routeLine = <<<HTML
-        <li>
-            <span><a href='%s' title="%s" style='font-family:monospace;font-size:.85em'>%s</a> - %s</span>
-            <span style='color:#ac1b1b'>%s</span> 
-            <small style="color:#374300">%s</small> 
-        </li>
-HTML;
+    protected SecurityHandler $securityHandler;
+
+    protected Taxus $taxus;
 
     public function __construct(
         public object $controller,
         protected UrlExtension $urlExtension,
-    ) {}
+        ContainerInterface $container
+    ) {
+        if ($container->has(SecurityHandler::class) && $container->has(Taxus::class)) {
+            $this->securityHandler = $container->get(SecurityHandler::class);
+            $this->taxus = $container->get(Taxus::class);
+        }
+    }
 
     public function getRoutes() : array
     {
@@ -47,6 +52,7 @@ HTML;
                     'description'=> $route->description,
                     #'methods' =>implode(', ', (array)$route->method),
                     'methods' => (array) $route->method,
+                    'privileges' => $this->getPrivilegeFromRoute($method->name),
                 ];
             }
         }
@@ -66,4 +72,32 @@ HTML;
 
         return implode('/', $paths);
     }
+
+    /**
+     * @param string $method    Which method are we inspecting
+     * @return false|array|null Returns NULL when securityHandler is undefined, FALSE when no security applies for this method and else an array of ['admin' => [ 'status' => true, 'description' => "' ], ...].
+     */
+    protected function getPrivilegeFromRoute(string $method) : null|false|array
+    {
+        if ( isset($this->securityHandler) ){
+            $list = [];
+
+            if ( $this->securityHandler->isLocked($this->controller::class, $method) ) {
+                foreach($this->taxus->list as $name => $definition) {
+                    if ($definition[0]->testableArguments !== null) {
+                        if ( $this->securityHandler->hasGrantPermission($this->controller::class, $method, ...$definition[0]->testableArguments) ) {
+
+                        }
+                    }
+                }
+            }
+            else {
+                return false;
+            }
+
+            return $list;
+        }
+
+        return false;
+    }
 }
\ No newline at end of file
diff --git a/view/lean-api/form_descriptor.phtml b/view/lean-api/form_descriptor.phtml
index 7681698..b2d1c97 100644
--- a/view/lean-api/form_descriptor.phtml
+++ b/view/lean-api/form_descriptor.phtml
@@ -6,8 +6,10 @@
 
 <div class="forms">
     {% foreach $forms as $name => $form %}
-        <div class="single-form" style="padding-left:15px;border-left: 3px solid #9ccce6;">
-            <h4 class='form-name'>{{ $name }}</h4>
+        <div class="single-form" style="padding-left:15px;border-left: 3px solid #9ccce6;" data-form="{% json.html $form %}">
+            <h4 class='form-name'>
+                <span>{{ $name }}</span>
+            </h4>
             <div class='description'>{{ $form['description'] }}</div>
             <hr style="margin-top:15px">
 
@@ -25,12 +27,24 @@
 
                         <div class="field-desc" style="margin-top:10px;;background:#fff;padding:5px;font-size:0.9em">
                             <div><u>Variable POST / champ JSON</u> : {{ $field['name'] }}</div>
-                            <div><u>Type(s)</u> : {{ $field['type'] }}</div>
+                            <div><u>Type(s)</u> : {{ implode(' | ', array_map(fn($e) => $e->type, $field['type'])) }}</div>
                             <div><u>Nullable</u> : {{ yesOrNo($field['allowNulls']) }}</div>
-                            <div><u>Pattern regex</u> : {{ $field['regexPattern'] }}</div>
-                            <div><u>Taille min.</u> : {{ $field['minLength'] }}</div>
-                            <div><u>Taille max.</u> : {{ $field['maxLength'] }}</div>
-                            <div><u>Exemple</u> : {{ $field['example'] }}</div>
+                            {% if $field['regexPattern'] !== null %}
+                                <div><u>Pattern regex</u> : {{ $field['regexPattern'] }}</div>
+                            {% endif %}
+                            {% if $field['minLength'] !== null %}
+                                <div><u>Taille min.</u> : {{ $field['minLength'] }}</div>
+                            {% endif %}
+                            {% if $field['maxLength'] !== null %}
+                                <div><u>Taille max.</u> : {{ $field['maxLength'] }}</div>
+                            {% endif %}
+                            {% if $field['example'] !== null || $field['values'] %}
+                                <div><u>Exemple</u> : {{ $field['example'] }}</div>
+                            {% endif %}
+
+                            {% if $field['values'] %}
+                                <div><u>Valeurs possibles</u> : [ <u>{{= implode('</u>, <u>', $field['values']) }}</u> ]</div>
+                            {% endif %}
                         </div>
                     </li>
                 {% endforeach %}
diff --git a/view/lean-api/request_debugger.phtml b/view/lean-api/request_debugger.phtml
new file mode 100644
index 0000000..0675d5d
--- /dev/null
+++ b/view/lean-api/request_debugger.phtml
@@ -0,0 +1,241 @@
+{% use Ulmus\Api\Common\MethodEnum %}
+
+{% language.set "lean.api.request.debugger" %}
+
+<div id="request-debugger" class="hide">
+    <div class="request-head">
+        <div class="method" style="">
+            {% ui.select "method", MethodEnum::generateArray(), post('method') %}
+        </div>
+
+        <div class="url" style="flex-grow: 1;">
+            {% ui:text "url" %}
+            <button class="request-btn">Envoyer</button>
+        </div>
+    </div>
+
+    <div class="request-content">
+        <div id="request" style="min-height: 150px; width:100%; border:1px solid #ededed;">{}</div>
+    </div>
+
+    <div class="request-response hide">
+        <hr>
+        <pre id="response" class="code" ace-theme="ace/theme/cloud9_day"></pre>
+
+        <div class="response-head">
+            <span class="response-code"></span>
+            <span class="response-message"></span>
+        </div>
+    </div>
+</div>
+
+<style>
+    .request-head {display:flex;padding-top: 4px;border:1px solid #e1e1e1;border-bottom:0}
+    .request-head .method {width: 90px;text-align: center;line-height: 34px;font-weight: bold;}
+    .request-head .method select {background:none;border:0;font-size: inherit;text-align: center;color: inherit;font-weight: bold;}
+    .request-head .url input {width: calc(100% - 94px); border:0;color: #6f6f6f;background: #f9f9f9;font-size: 80%;font-weight: bold;}
+    .request-head .url button {font-size: 80%;width: 80px;border-radius:0;border: 1px solid #ccc;height:26px;padding: 0;background:#f9f9f9;color: #6f6f6f;cursor: pointer;}
+    .request-head .url button:active {filter:contrast(85%);}
+
+    .response-head {display: flex;border:1px solid #9f9f9f;margin-bottom: 5px;}
+    .response-head .response-code {background:#dfdfdf;padding:0 5px;font-weight: bold;line-height: 29px;font-size: 80%;}
+    .response-head .response-message {padding:0 10px;background:#fbfbfb;line-height: 30px;font-size: 80%;}
+
+    #response {max-height: 66vh;}
+</style>
+
+<script src="{% asset 'static/ace/src-noconflict/ace.js' %}" type="text/javascript" charset="utf-8"></script>
+<script src="{% asset 'static/ace/src-noconflict/ext-static_highlight.js' %}" type="text/javascript" charset="utf-8"></script>
+
+<script>
+    let requestDebugger = document.getElementById('request-debugger'),
+        requestHead = requestDebugger.querySelector(".request-head"),
+        requestContent = requestDebugger.querySelector(".request-content"),
+        responseHead = requestDebugger.querySelector(".response-head"),
+        responseResponse = requestDebugger.querySelector(".request-response"),
+        method = requestHead.querySelector(".method"),
+        input = requestHead.querySelector("[name='url']"),
+        button = requestHead.querySelector(".request-btn");
+
+    // Editor
+    let editor = ace.edit("request");
+    editor.setTheme("ace/theme/cloud9_day");
+    editor.session.setMode("ace/mode/json");
+
+    // Code highlighter
+    let highlight = ace.require("ace/ext/static_highlight"),
+        dom = ace.require("ace/lib/dom"),
+        responseEditorElement = responseResponse.querySelector('#response');
+
+    input.addEventListener('keydown', evt => evt.keyCode === 13 ? button.click() : null);
+
+    document.addEventListener("DOMContentLoaded", (evt) => {
+        document.querySelectorAll(".form-name").forEach(elem => {
+            elem.insertAdjacentHTML('beforeend', '<a href="javascript:void(0)" class="btn debug">DEBUG</a>');
+
+            let formData = JSON.parse(elem.closest('.single-form').getAttribute('data-form'));
+
+            elem.querySelector(".btn.debug").addEventListener("click", (btn) => {
+                requestDebugger.classList.toggle('hide', false);
+
+                let json = {};
+
+                formData.fields.forEach((field) => {
+                    json[field.name] = field.allowNulls ? null : "";
+                });
+
+                editor.setValue(JSON.stringify(json,null,2), -1);
+            });
+        });
+
+        document.querySelectorAll("li[class*='method-']").forEach( (url) => {
+            url.addEventListener("click", (evt) => {
+                evt.preventDefault();
+
+                let routeMethod = url.querySelector('.route-method');
+
+                method.style.color = getComputedStyle(routeMethod).getPropertyValue('background-color');
+                method.querySelector('select').value = routeMethod.innerText;
+
+                input.value = url.querySelector('.route-link a').getAttribute('href');
+
+                document.getElementById('request-debugger').classList.toggle('hide', false);
+
+                replaceUrlVariableUsingData();
+
+                input.focus();
+            });
+        });
+
+        let aceMode;
+
+        button.addEventListener("click", (evt) => {
+            let requestMethod = method.querySelector('select').value;
+
+            if (requestMethod === 'DELETE' && ! confirm("Attention ! Vous allez lancer une procédure de suppression de données.\n\nContinuer ?")) {
+                return;
+            }
+
+            launchRequest(requestMethod, input.value, editor.getValue())
+            .then((response) => {
+                responseHead.querySelector('.response-code').innerText = response.status;
+                responseHead.querySelector('.response-message').innerText = response.statusText;
+
+                aceMode = parseContentType(response);
+
+                return response.text();
+            })
+            .then(body => {
+                if (aceMode === "json") {
+                    body = JSON.stringify(JSON.parse(body), null, 2);
+                    responseEditorElement.innerHTML = body;
+                }
+                else {
+                    responseEditorElement.innerText = body;
+                }
+
+                formatResponse("ace/mode/" + aceMode);
+            });
+
+            responseResponse.classList.toggle('hide', false);
+        });
+
+        let selectRoute = getQueryVariable("debug.route"),
+            presetData = getQueryVariable("debug.data");
+
+        if ( selectRoute ) {
+            if (presetData) {
+                editor.setValue(presetData);
+            }
+
+            document.querySelector(`[data-name="${selectRoute}"]`).click();
+        }
+    });
+
+    async function launchRequest(method = "POST", url = "", body = "{}") {
+        method = method.toUpperCase();
+
+        let responseData = {
+            method: method,
+            mode: "cors",
+            cache: "no-cache",
+            credentials: "same-origin",
+            headers: {
+                "Content-Type": "application/json",
+                'Authorization': 'Bearer {{ $this->session->jwt }}'
+            },
+            redirect: "follow",
+            referrerPolicy: "no-referrer"
+        };
+
+        if ( [ "HEAD", "GET" ].indexOf(method) === -1 ) {
+            responseData.body = body;
+        }
+
+        return await fetch(url, responseData);
+    }
+
+    function formatResponse(mode) {
+        highlight(responseEditorElement, {
+            mode: mode,
+            theme: responseEditorElement.getAttribute("ace-theme"),
+            firstLineNumber: 1,
+            showGutter: responseEditorElement.getAttribute("ace-gutter"),
+            trim: true
+        });
+    }
+
+    function parseContentType(response) {
+        let type = response.headers.get('content-type').toLowerCase();
+
+        if ( type.indexOf('text/html') !== -1 ) {
+            return "html";
+        }
+        else if ( type.indexOf('text/css') !== -1 ) {
+            return "css";
+        }
+        else if ( type.indexOf('text/csv') !== -1 ) {
+            return "csv";
+        }
+        else if ( type.indexOf('text/xml') !== -1 ) {
+            return "xml";
+        }
+        else if ( type.indexOf('text/plain') !== -1 ) {
+            return "text";
+        }
+        else if ( type.indexOf('application/javascript') !== -1 ) {
+            return "javascript";
+        }
+        else if ( type.indexOf('application/json') !== -1 || type.indexOf('application/ld+json') !== -1 ) {
+            return "json";
+        }
+    }
+
+    function getQueryVariable(variable) {
+        retval = false;
+
+        window.location.search.substring(1).split("&").forEach(vars => {
+            var pair = vars.split("=");
+
+            if (pair.length === 2 && pair[0] === variable) {
+                return retval = decodeURI(pair[1]);
+            }
+        });
+
+        return retval;
+    }
+
+    function replaceUrlVariableUsingData() {
+        try {
+            let vars = input.value.match(/[^{}]+(?=})/g),
+                body = JSON.parse(editor.getValue());
+
+            vars.forEach(v => {
+                if (body[v]) {
+                    input.value = input.value.replace("{" + v + "}", body[v]);
+                }
+            })
+        }
+        catch(e) {}
+    }
+</script>
\ No newline at end of file
diff --git a/view/lean-api/route_descriptor.phtml b/view/lean-api/route_descriptor.phtml
index bc9fb28..06fed63 100644
--- a/view/lean-api/route_descriptor.phtml
+++ b/view/lean-api/route_descriptor.phtml
@@ -3,19 +3,33 @@
 <ul class="routes-wrapper">
     {% foreach $routes as $route %}
         {% foreach $route['methods'] as $method %}
-            <li class="method-{{ strtolower($method) }}">
+            <li class="method-{{ strtolower($method) }}" data-name="{{ $route['name'] }}">
                 <span class="route-method">
                     <span class="method-name">{{ strtoupper($method) }}</span>
                 </span>
+
                 <span class="route-link">
                     <a href="{{ $route['route'] }}" title="{{ $route['path'] }}">{{ $route['cleaned'] }}</a>
                     <span>-</span>
                     <span>{{= $route['description'] }}</span>
                 </span>
-                <small class="route-name">{{ $route['name'] }}</small>
+
+                <small class="route-name">
+                    <span>{{ $route['name'] }}</span>
+                    <br>
+                    <span class="permissions">
+                        {% foreach ['admin', 'school' ] as $privilege %}
+                            <u class="privilege">{{ $privilege }}</u>
+                        {% endforeach %}
+                    </span>
+                </small>
             </li>
         {% or %}
             <i style="color:#585858; padding:0 12px">{% _ "none" %}</i>
         {% endforeach %}
     {% endforeach %}
-</ul>
\ No newline at end of file
+</ul>
+
+<style>
+    .permissions {color:#888}
+</style>
\ No newline at end of file
diff --git a/view/lean/layout/docs.phtml b/view/lean/layout/docs.phtml
index b838b8b..267738a 100644
--- a/view/lean/layout/docs.phtml
+++ b/view/lean/layout/docs.phtml
@@ -16,6 +16,7 @@
             <meta name="theme-color" content="#ffffff">
 
             <style>
+                *, *:before, *:after {box-sizing: border-box;}
                 a{color:#e44a00}h3{background:#e1e1e1;padding:6px 12px}ul{background:#e9e9fd;padding-top:20px;padding-bottom:20px;border:1px solid #e3e3ec}ul li + li{margin-top:8px}li > em{font-size:0.75rem;color:gray}body{font-family:Helvetica, 'Helvetica Neuve', Arial, Tahoma, sans-serif;font-size:17px;color:#333}h1,h2,h3,h4,h5,h6{color:#222;margin:0 0 20px}dl,ol,p,pre,table,ul{margin:0 0 20px}h1,h2,h3{line-height:1.1}h1{font-size:20px;text-align:right;color:#387eea;font-weight:bold}h2{color:#393939}h3,h4,h5,h6{color:#494949}h3{display:flex}h3 > code{margin-right:5px;color:#b52dac}h3 > strong{margin-left:auto}a{color:#39c;font-weight:400;text-decoration:none}a small{font-size:11px;color:#777;margin-top:-0.6em;display:block}.wrapper{width:860px;margin:0 auto}blockquote{border-left:1px solid #e5e5e5;margin:0;padding:0 0 0 20px;font-style:italic}code,pre{font-size:12px}pre{padding:8px 15px;background:#f8f8f8;border-radius:5px;border:1px solid #e5e5e5;overflow-x:auto}table{width:100%;border-collapse:collapse}td,th{text-align:left;padding:5px 10px;border-bottom:1px solid #e5e5e5}dt{color:#444;font-weight:700}th{color:#444}img{max-width:100%}header{width:270px;float:left;position:fixed}header ul{list-style:none;height:40px;padding:0;background:#eee;background:-moz-linear-gradient(top, #f8f8f8 0%, #dddddd 100%);background:-webkit-gradient(linear, left top, left bottom, color-stop(0%,#f8f8f8), color-stop(100%,#dddddd));background:-webkit-linear-gradient(top, #f8f8f8 0%,#dddddd 100%);background:-o-linear-gradient(top, #f8f8f8 0%,#dddddd 100%);background:-ms-linear-gradient(top, #f8f8f8 0%,#dddddd 100%);background:linear-gradient(top, #f8f8f8 0%,#dddddd 100%);border-radius:5px;border:1px solid #d2d2d2;box-shadow:inset #fff 0 1px 0, inset rgba(0,0,0,0.03) 0 -1px 0;width:270px}header li{width:89px;float:left;border-right:1px solid #d2d2d2;height:40px}header ul a{line-height:1;font-size:11px;color:#999;display:block;text-align:center;padding-top:6px;height:40px}strong{color:#222;font-weight:700}header ul li + li{width:88px;border-left:1px solid #fff}header ul li + li + li{border-right:none;width:89px}header ul a strong{font-size:14px;display:block;color:#222}section{width:500px;float:right;padding-bottom:50px}small{font-size:11px}hr{border:0;background:#e5e5e5;height:1px;margin:0 0 20px}footer{width:270px;float:left;position:fixed;bottom:50px}@media print, screen and (max-width: 960px){div.wrapper{width:auto;margin:0}footer,header,section{float:none;position:static;width:auto}header{padding-right:320px}section{border:1px solid #e5e5e5;border-width:1px 0;padding:20px 0;margin:0 0 20px}header a small{display:inline}header ul{position:absolute;right:50px;top:52px}}@media print, screen and (max-width: 720px){body{word-wrap:break-word}header{padding:0}header p.view,header ul{position:static}code,pre{word-wrap:normal}}@media print, screen and (max-width: 480px){body{padding:15px}header ul{display:none}}@media print{body{padding:0.4in;font-size:12pt;color:#444}}#wrapper{margin-left:auto;margin-right:auto;background-color:white}.ca-menu{list-style:none;padding:0;margin:20px auto}#navi{padding-top:15px;padding-right:15px;float:right;width:420px}#title{padding-left:15px;width:460px;float:left}div.clear{clear:both}h2{font-size:2em}h3{font-size:1.5em}h4{font-size:1.2em}h5{font-size:1em;font-weight:bold}h6{font-size:1em;font-weight:bold}h1,h2,h3,h4,h5,h6{font-weight:normal;line-height:2.5rem;margin:1rem 0}.post p{max-width:580px}ol.list,ul.list{padding-left:3.333em;max-width:580px}.post h2{border-bottom:1px solid #EDEDED}h1:nth-child(1),h2:nth-child(1),h3:nth-child(1),h4:nth-child(1),h5:nth-child(1),h6:nth-child(1){margin-top:0}body{padding:1em}#wrapper{padding:1em}@media (min-width: 43.75em){body{padding:2em}#wrapper{padding:2em}}@media (min-width: 62em){body{padding:3em}#wrapper{max-width:740px;padding:3em}}
                 ol{background: #eff4f2;padding-top:20px;padding-bottom:20px;border:1px solid #e3e3ec}
                 h4{background:#e0f7ed;padding:6px 12px; font-weight: bold!important;font-size:100%;margin-top:0}
@@ -23,7 +24,9 @@
                 h3 {display: flex;align-items: center;height: 60px;padding: 0 15px 0 15px;font-variant: small-caps;}
                 li.odd-even{border-top:1px solid #ccc;margin:10px 0;padding:15px 15px 10px 5px}
                 li.odd-even:first-child{border:0}
-                input, button {padding:5px; font-size:1em;margin-top:10px}
+                input, button {padding:5px; font-size:1em;margin:0}
+
+                .hide {display:none!important}
 
                 ul {background:#f4f4f4; list-style: none; padding-left:20px}
                 ul ul {margin: 0;border: 0;padding: 5px 30px;}
@@ -32,16 +35,19 @@
                 .field-desc > div {padding:5px;}
                 .forms ol {background: #ccdef2;}
                 .forms li {border-color: #859aae;}
-                .forms .form-name {background: #9cc5e6;color: #284168;font-size:110%}
+                .forms .form-name {background: #9cc5e6;display:flex;justify-content: space-between}
+                .forms .form-name span {color: #284168;font-size:110%}
+                .forms .form-name .btn {background:rgba(50,50,50,0.5);padding:0 15px;border: 1px solid rgba(50,50,50,0.8);font-family: 'Déja Vu', 'Courier New', Courier, monospace, serif;font-size: 90%;color: #fff;text-decoration: underline;}
 
                 .routes-wrapper {padding:8px}
-                .routes-wrapper li {display:flex;border: 1px solid #ccc;align-items: stretch;}
+                .routes-wrapper li {display:flex;border: 1px solid #ccc;align-items: stretch;cursor:pointer}
+                .routes-wrapper li:hover {filter: contrast(85%)}
                 .routes-wrapper li + li {margin-top: 5px;}
                 .routes-wrapper .route-method {display:flex;align-items: center; justify-content: center; line-height: 1.8rem;padding:6px 5px 0 5px;min-width:80px;text-align:center;font-weight:bold;color:#fff;}
                 .routes-wrapper .route-link {line-height: 1.8rem;padding:0 10px;display: flex;align-items: center;}
                 .routes-wrapper .route-link span {margin-left: 7px;}
-                .routes-wrapper .route-link a {font-family:monospace;font-size:.85em}
-                .routes-wrapper .route-name {margin-left:auto;font-weight:bold;min-width: 20%;text-align: right;background:rgba(0, 0, 0, 0.02);line-height: 1.8rem;padding:6px 7px 0 5px;}
+                .routes-wrapper .route-link a {font-family:monospace;font-size:.85em;white-space: nowrap;}
+                .routes-wrapper .route-name {margin-left:auto;font-weight:bold;min-width: 20%;text-align: right;background:rgba(0, 0, 0, 0.02);line-height: 1.4rem;padding:6px 7px 0 5px;align-content: center;}
                 .routes-wrapper li.method-get {background:#e7eff7;border-color: #bfcfdd;}
                 .routes-wrapper li.method-get .route-method {background:#0f6ab4;}
                 .routes-wrapper li.method-get .route-link a, .routes-wrapper li.method-get .route-name {color: #0f6ab4;}