Merge branch 'master' into NLPCRAFT-369
diff --git a/nlpcraft/src/main/scala/org/apache/nlpcraft/common/util/NCUtils.scala b/nlpcraft/src/main/scala/org/apache/nlpcraft/common/util/NCUtils.scala
index 4503970..a1c8f97 100644
--- a/nlpcraft/src/main/scala/org/apache/nlpcraft/common/util/NCUtils.scala
+++ b/nlpcraft/src/main/scala/org/apache/nlpcraft/common/util/NCUtils.scala
@@ -1667,6 +1667,10 @@
         else
             try
                 GSON.toJson(GSON.getAdapter(classOf[JsonElement]).fromJson(json))
+                    // Fix the problem with escaping '<' and '>' which is only
+                    // a theoretical problem for browsers displaying JSON.
+                    .replace("\\u003c", "<")
+                    .replace("\\u003e", ">")
             catch {
                 case _: Exception => ""
             }
diff --git a/nlpcraft/src/main/scala/org/apache/nlpcraft/model/NCModelView.java b/nlpcraft/src/main/scala/org/apache/nlpcraft/model/NCModelView.java
index 4a6e3a5..edd9561 100644
--- a/nlpcraft/src/main/scala/org/apache/nlpcraft/model/NCModelView.java
+++ b/nlpcraft/src/main/scala/org/apache/nlpcraft/model/NCModelView.java
@@ -159,6 +159,11 @@
     int MODEL_VERSION_MAXLEN = 16;
 
     /**
+     * Max length for {@link NCElement#getId()} method.
+     */
+    int MODEL_ELEMENT_ID_MAXLEN = 64;
+
+    /**
      * Default value for {@link #isSparse()} method.
      */
     boolean DFLT_IS_SPARSE = false;
diff --git a/nlpcraft/src/main/scala/org/apache/nlpcraft/model/tools/cmdline/NCCli.scala b/nlpcraft/src/main/scala/org/apache/nlpcraft/model/tools/cmdline/NCCli.scala
index 401ec67..549372d 100644
--- a/nlpcraft/src/main/scala/org/apache/nlpcraft/model/tools/cmdline/NCCli.scala
+++ b/nlpcraft/src/main/scala/org/apache/nlpcraft/model/tools/cmdline/NCCli.scala
@@ -147,7 +147,9 @@
     private final val REST_CMD = CMDS.find(_.name == "rest").get
     private final val CALL_CMD = CMDS.find(_.name == "call").get
     private final val ASK_CMD = CMDS.find(_.name == "ask").get
-    private final val SUGSYN_CMD = CMDS.find(_.name == "sugsyn").get
+    private final val MODEL_SUGSYN_CMD = CMDS.find(_.name == "model-sugsyn").get
+    private final val MODEL_SYNS_CMD = CMDS.find(_.name == "model-syns").get
+    private final val MODEL_INFO_CMD = CMDS.find(_.name == "model-info").get
 
     /**
      * @param cmd
@@ -549,6 +551,7 @@
                     logln(r(" [Error]"))
 
                     error(s"Server start failed - check full log for errors: ${c(output.getAbsolutePath)}")
+                    error(s"If the problem persists - remove ${c("~/.nlpcraft")} folder and try again.")
 
                     tailFile(output.getAbsolutePath, 20)
                 }
@@ -1555,6 +1558,7 @@
      */
     private [cmdline] def cmdInfo(cmd: Command, args: Seq[Argument], repl: Boolean): Unit = {
         cmdInfoServer(CMDS.find(_.name == "info-server").get, Seq.empty[Argument], repl)
+        logln()
         cmdInfoProbe(CMDS.find(_.name == "info-probe").get, Seq.empty[Argument], repl)
     }
 
@@ -1737,7 +1741,7 @@
      * @param args Arguments, if any, for this command.
      * @param repl Whether or not executing from REPL.
      */
-    private [cmdline] def cmdSugSyn(cmd: Command, args: Seq[Argument], repl: Boolean): Unit =
+    private [cmdline] def cmdModelSugSyn(cmd: Command, args: Seq[Argument], repl: Boolean): Unit =
         state.accessToken match {
             case Some(acsTok) =>
                 val mdlId = getParamOpt(args, "mdlId") match {
@@ -1771,6 +1775,72 @@
      * @param args Arguments, if any, for this command.
      * @param repl Whether or not executing from REPL.
      */
+    private [cmdline] def cmdModelSyns(cmd: Command, args: Seq[Argument], repl: Boolean): Unit =
+        state.accessToken match {
+            case Some(acsTok) =>
+                val mdlId = getParamOpt(args, "mdlId") match {
+                    case Some(id) => id
+                    case None =>
+                        if (state.probes.size == 1 && state.probes.head.models.length == 1)
+                            state.probes.head.models.head.id
+                        else
+                            throw MissingOptionalParameter(cmd, "mdlId")
+                }
+                val elmId = getParam(cmd, args, "elmId")
+
+                httpRest(
+                    cmd,
+                    "model/syns",
+                    s"""
+                       |{
+                       |    "acsTok": ${jsonQuote(acsTok)},
+                       |    "mdlId": ${jsonQuote(mdlId)},
+                       |    "elmId": ${jsonQuote(elmId)}
+                       |}
+                       |""".stripMargin
+                )
+
+            case None => throw NotSignedIn()
+        }
+
+    /**
+     *
+     * @param cmd Command descriptor.
+     * @param args Arguments, if any, for this command.
+     * @param repl Whether or not executing from REPL.
+     */
+    private [cmdline] def cmdModelInfo(cmd: Command, args: Seq[Argument], repl: Boolean): Unit =
+        state.accessToken match {
+            case Some(acsTok) =>
+                val mdlId = getParamOpt(args, "mdlId") match {
+                    case Some(id) => id
+                    case None =>
+                        if (state.probes.size == 1 && state.probes.head.models.length == 1)
+                            state.probes.head.models.head.id
+                        else
+                            throw MissingOptionalParameter(cmd, "mdlId")
+                }
+
+                httpRest(
+                    cmd,
+                    "model/info",
+                    s"""
+                       |{
+                       |    "acsTok": ${jsonQuote(acsTok)},
+                       |    "mdlId": ${jsonQuote(mdlId)}
+                       |}
+                       |""".stripMargin
+                )
+
+            case None => throw NotSignedIn()
+        }
+
+    /**
+     *
+     * @param cmd Command descriptor.
+     * @param args Arguments, if any, for this command.
+     * @param repl Whether or not executing from REPL.
+     */
     private [cmdline] def cmdAsk(cmd: Command, args: Seq[Argument], repl: Boolean): Unit =
         state.accessToken match {
             case Some(acsTok) =>
@@ -2520,7 +2590,7 @@
 
                                     mkCandidate(
                                         disp = name,
-                                        grp = s"REST ${cmd.group}:",
+                                        grp = MANDATORY_GRP,
                                         desc = cmd.desc,
                                         completed = true
                                     )
@@ -2529,20 +2599,50 @@
                             )
                     }
 
-                    // For 'ask' and 'sugysn' - add additional model IDs auto-completion/suggestion candidates.
-                    if (cmd == ASK_CMD.name || cmd == SUGSYN_CMD.name)
+                    // For 'ask', 'sugsyn', 'model-syn' and 'model-info' - add additional
+                    // model IDs auto-completion/suggestion candidates.
+                    if (cmd == ASK_CMD.name || cmd == MODEL_SUGSYN_CMD.name || cmd == MODEL_SYNS_CMD.name || cmd == MODEL_INFO_CMD.name)
                         candidates.addAll(
-                            state.probes.flatMap(_.models.toList).map(mdl => {
+                            state.probes.flatMap(_.models.toList).map(mdl =>
                                 mkCandidate(
                                     disp = s"--mdlId=${mdl.id}",
                                     grp = MANDATORY_GRP,
                                     desc = null,
                                     completed = true
                                 )
-                            })
+                            )
                             .asJava
                         )
 
+                    // For 'model-syns' add auto-completion for element IDs.
+                    if (cmd == MODEL_SYNS_CMD.name) {
+                        val mdlIdParam = MODEL_SYNS_CMD.findParameterById("mdlId")
+
+                        words.find(w => mdlIdParam.names.exists(x => w.startsWith(x))) match {
+                            case Some(p) =>
+                                val mdlId = p.substring(p.indexOf('=') + 1)
+
+                                state.probes.flatMap(_.models).find(_.id == mdlId) match {
+                                    case Some(mdl) =>
+                                        candidates.addAll(
+                                            mdl.elementIds.toList.map(id =>
+                                                mkCandidate(
+                                                    disp = s"--elmId=$id",
+                                                    grp = MANDATORY_GRP,
+                                                    desc = null,
+                                                    completed = true
+                                                )
+                                            )
+                                            .asJava
+                                        )
+
+                                    case None => ()
+                                }
+
+                            case None => ()
+                        }
+                    }
+
                     // For 'call' - add additional auto-completion/suggestion candidates.
                     if (cmd == CALL_CMD.name) {
                         val pathParam = CALL_CMD.findParameterById("path")
diff --git a/nlpcraft/src/main/scala/org/apache/nlpcraft/model/tools/cmdline/NCCliBase.scala b/nlpcraft/src/main/scala/org/apache/nlpcraft/model/tools/cmdline/NCCliBase.scala
index 96c219c..0bd7531 100644
--- a/nlpcraft/src/main/scala/org/apache/nlpcraft/model/tools/cmdline/NCCliBase.scala
+++ b/nlpcraft/src/main/scala/org/apache/nlpcraft/model/tools/cmdline/NCCliBase.scala
@@ -121,10 +121,11 @@
 
     // See NCProbeModelMdo.
     case class ProbeModel(
-         id: String,
-         name: String,
-         version: String,
-         enabledBuiltInTokens: Array[String]
+        id: String,
+        name: String,
+        version: String,
+        elementIds: Array[String],
+        enabledBuiltInTokens: Array[String]
     )
 
     case class ProbeAllResponse(
diff --git a/nlpcraft/src/main/scala/org/apache/nlpcraft/model/tools/cmdline/NCCliCommands.scala b/nlpcraft/src/main/scala/org/apache/nlpcraft/model/tools/cmdline/NCCliCommands.scala
index 0f86e13..3b43ef0 100644
--- a/nlpcraft/src/main/scala/org/apache/nlpcraft/model/tools/cmdline/NCCliCommands.scala
+++ b/nlpcraft/src/main/scala/org/apache/nlpcraft/model/tools/cmdline/NCCliCommands.scala
@@ -95,6 +95,7 @@
     private final lazy val SCRIPT_NAME = U.sysEnv("NLPCRAFT_CLI_SCRIPT").getOrElse(s"nlpcraft.${if (SystemUtils.IS_OS_UNIX) "sh" else "cmd"}")
     private final lazy val PROMPT = if (SCRIPT_NAME.endsWith("cmd")) ">" else "$"
     private final lazy val VER = NCVersion.getCurrent
+    private final lazy val REST_SPEC_URL = "https://nlpcraft.apache.org/using-rest.html"
 
     //noinspection DuplicatedCode
     // All supported commands.
@@ -104,7 +105,7 @@
             group = "2. REST Commands",
             synopsis = s"REST call in a convenient way for command line mode.",
             desc = Some(
-                s"When using this command you supply all call parameters as a single ${c("'--json'")} parameter with a JSON string. " +
+                s"When using this command you supply all call parameters as a single ${y("'--json'")} parameter with a JSON string. " +
                 s"In REPL mode, you can hit ${rv(" Tab ")} to see auto-suggestion and auto-completion candidates for " +
                 s"commonly used paths. However, ${y("'call'")} command provides more convenient way to issue REST " +
                 s"calls when in REPL mode."
@@ -158,6 +159,7 @@
             group = "2. REST Commands",
             synopsis = s"Wrapper for ${y("'/signin'")} REST call.",
             desc = Some(
+                s"See ${REST_SPEC_URL} for REST call specification. " +
                 s"If no arguments provided, it signs in with the " +
                 s"default 'admin@admin.com' user account. NOTE: please make sure to remove this account when " +
                 s"running in production."
@@ -195,6 +197,7 @@
             group = "2. REST Commands",
             synopsis = s"Wrapper for ${y("'/signout'")} REST call in REPL mode.",
             desc = Some(
+                s"See ${REST_SPEC_URL} for REST call specification. " +
                 s"Signs out currently signed in user. Note that this command makes sense only in REPL mode."
             ),
             body = NCCli.cmdSignOut,
@@ -246,18 +249,18 @@
             examples = Seq(
                 Example(
                     usage = Seq(
-                        s"$PROMPT $SCRIPT_NAME call -p=signin",
+                        s"> call -p=signin",
                         "  --email=admin@admin.com",
                         "  --passwd=admin"
                     ),
                     desc =
                         s"Issues ${y("'signin'")} REST call with given JSON payload provided as a set of parameters. " +
-                        s"Note that ${c("'--email'")} and ${c("'--passwd'")} parameters correspond to the REST call " +
+                        s"Note that ${y("'--email'")} and ${y("'--passwd'")} parameters correspond to the REST call " +
                         s"specification for ${y("'/signin'")} path."
                 ),
                 Example(
                     usage = Seq(
-                        s"$$ nlpcraft.sh call --path=ask/sync",
+                        s"> call --path=ask/sync",
                         "  --acsTok=qwerty123456",
                         "  --txt=\"User request\"",
                         "  --mdlId=my.model.id",
@@ -265,28 +268,16 @@
                         "  --enableLog=false"
                     ),
                     desc =
-                        s"${bo("Unix/Linux:")} issues ${y("'ask/sync'")} REST call with given JSON payload provided as a set of parameters."
-                ),
-                Example(
-                    usage = Seq(
-                        s"> nlpcraft.cmd call --path=ask/sync",
-                        "  --acsTok=qwerty123456",
-                        "  --txt=\"User request\"",
-                        "  --mdlId=my.model.id",
-                        "  --data='{\\\"data1\\\": true, \\\"data2\\\": 123, \\\"data3\\\": \\\"some text\\\"}'",
-                        "  --enableLog=false"
-                    ),
-                    desc =
-                        s"${bo("Windows:")} issues ${y("'ask/sync'")} REST call with given JSON payload provided " +
-                        s"as a set of parameters. Note the necessary double quote escaping."
+                        s"Issues ${y("'ask/sync'")} REST call with given JSON payload provided as a set of parameters."
                 )
             )
         ),
         Command(
             name = "ask",
             group = "2. REST Commands",
-            synopsis = s"Wrapper for ${c("'/ask/sync'")} REST call.",
+            synopsis = s"Wrapper for ${y("'/ask/sync'")} REST call.",
             desc = Some(
+                s"See ${REST_SPEC_URL} for REST call specification. " +
                 s"Requires user to be already signed in. This command ${bo("only makes sense in the REPL mode")} as " +
                 s"it requires user to be signed in. REPL session keeps the currently active access " +
                 s"token after user signed in. For command line mode, use ${y("'rest'")} command with " +
@@ -369,7 +360,7 @@
                     value = Some("class"),
                     desc =
                         s"Mandatory JDBC driver class. Note that 'class' must be a fully qualified class name. " +
-                        s"It should also be available on the classpath or provided additionally via ${c("'--cp'")} parameter."
+                        s"It should also be available on the classpath or provided additionally via ${y("'--cp'")} parameter."
                 ),
                 Parameter(
                     id = "schema",
@@ -397,9 +388,9 @@
                         s"Additional JVM classpath that will be appended to the default NLPCraft JVM classpath. " +
                         s"Parameter should include one or more classpath entry (JAR or directory) separated by the OS specific classpath separator. " +
                         s"Although this configuration property is optional, in most cases you will need to provide an " +
-                        s"additional classpath for JDBC driver that you use (see ${c("'--driver'")} parameter) unless " +
+                        s"additional classpath for JDBC driver that you use (see ${y("'--driver'")} parameter) unless " +
                         s"it is available in NLPCraft by default, i.e. Apache Ignite and H2. " +
-                        s"Note that you can have multiple ${c("'--cp'")} parameters and their values will be " +
+                        s"Note that you can have multiple ${y("'--cp'")} parameters and their values will be " +
                         s"automatically combined into one final additional classpath. " +
                         s"Note also that you can use ${y("'~'")} at the beginning of the classpath component to specify user home directory."
                 ),
@@ -454,7 +445,7 @@
                     optional = true,
                     desc =
                         s"Semicolon-separate list of tables and/or columns to exclude. By default, none of the " +
-                        s"tables and columns in the schema are excluded. See ${c("'--help'")} parameter to get more details."
+                        s"tables and columns in the schema are excluded. See ${y("'--help'")} parameter to get more details."
                 ),
                 Parameter(
                     id = "include",
@@ -463,7 +454,7 @@
                     optional = true,
                     desc =
                         s"Semicolon-separate list of tables and/or columns to include. By default, all of the " +
-                        s"tables and columns in the schema are included. See ${c("'--help'")} parameter to get more details."
+                        s"tables and columns in the schema are included. See ${y("'--help'")} parameter to get more details."
                 ),
                 Parameter(
                     id = "prefix",
@@ -555,10 +546,91 @@
             )
         ),
         Command(
-            name = "sugsyn",
+            name = "model-info",
+            group = "2. REST Commands",
+            synopsis = s"Wrapper for ${y("'/model/info'")} REST call.",
+            desc = Some(
+                s"See ${REST_SPEC_URL} for REST call specification. " +
+                s"Requires user to be already signed in. This command ${bo("only makes sense in the REPL mode")} as " +
+                s"it requires user to be signed in. REPL session keeps the currently active access " +
+                s"token after user signed in. For command line mode, use ${y("'rest'")} command with " +
+                s"corresponding parameters. Note also that it requires a local probe running that hosts " +
+                s"the specified model."
+            ),
+            body = NCCli.cmdModelInfo,
+            params = Seq(
+                Parameter(
+                    id = "mdlId",
+                    names = Seq("--mdlId", "-m"),
+                    value = Some("model.id"),
+                    optional = true,
+                    desc =
+                        s"ID of the model to get configuration properties and elements for. " +
+                        s"In REPL mode, hit ${rv(" Tab ")} to see auto-suggestion for possible model IDs. Note that " +
+                        s"this is optional ONLY if there is only one connected probe and it has only one model deployed - which will be " +
+                        s"used by default. In all other cases - this parameter is mandatory."
+                )
+            ),
+            examples = Seq(
+                Example(
+                    usage = Seq(
+                        s"""> model-info -m=my.model.id"""
+                    ),
+                    desc =
+                        s"Issues ${y("'model/info'")} REST call with given model ID."
+                )
+            )
+        ),
+        Command(
+            name = "model-syns",
+            group = "2. REST Commands",
+            synopsis = s"Wrapper for ${y("'/model/syns'")} REST call.",
+            desc = Some(
+                s"See ${REST_SPEC_URL} for REST call specification. " +
+                s"Requires user to be already signed in. This command ${bo("only makes sense in the REPL mode")} as " +
+                s"it requires user to be signed in. REPL session keeps the currently active access " +
+                s"token after user signed in. For command line mode, use ${y("'rest'")} command with " +
+                s"corresponding parameters. Note also that it requires a local probe running that hosts " +
+                s"the specified model."
+            ),
+            body = NCCli.cmdModelSyns,
+            params = Seq(
+                Parameter(
+                    id = "mdlId",
+                    names = Seq("--mdlId", "-m"),
+                    value = Some("model.id"),
+                    optional = true,
+                    desc =
+                        s"ID of the model to get expanded synonyms for. " +
+                        s"In REPL mode, hit ${rv(" Tab ")} to see auto-suggestion for possible model IDs. Note that " +
+                        s"this is optional ONLY if there is only one connected probe and it has only one model deployed - which will be " +
+                        s"used by default. In all other cases - this parameter is mandatory."
+                ),
+                Parameter(
+                    id = "elmId",
+                    names = Seq("--elmId", "-e"),
+                    value = Some("element.id"),
+                    desc =
+                        s"ID of the model element to get expanded synonyms and values for. " +
+                        s"In REPL mode, hit ${rv(" Tab ")} to see auto-suggestion for possible element IDs."
+                )
+            ),
+            examples = Seq(
+                Example(
+                    usage = Seq(
+                        s"""> model-syns -m=my.model.id -e=my:elem"""
+                    ),
+                    desc =
+                        s"Issues ${y("'model/syns'")} REST call with given model and element IDs."
+                )
+            )
+        ),
+        Command(
+            name = "model-sugsyn",
             group = "2. REST Commands",
             synopsis = s"Wrapper for ${y("'/model/sugsyn'")} REST call.",
             desc = Some(
+                s"See ${REST_SPEC_URL} for REST call specification. " +
                 s"Requires user to be already signed in. This command ${bo("only makes sense in the REPL mode")} as " +
                 s"it requires user to be signed in. REPL session keeps the currently active access " +
                 s"token after user signed in. For command line mode, use ${y("'rest'")} command with " +
@@ -566,7 +638,7 @@
                 s"the specified model as well as running ${y("'ctxword'")} server. Find more information about " +
                 s"this tool at https://nlpcraft.apache.org/tools/syn_tool.html"
             ),
-            body = NCCli.cmdSugSyn,
+            body = NCCli.cmdModelSugSyn,
             params = Seq(
                 Parameter(
                     id = "mdlId",
@@ -590,14 +662,14 @@
             examples = Seq(
                 Example(
                     usage = Seq(
-                        s"""> sugsyn -m=my.model.id"""
+                        s"""> model-sugsyn -m=my.model.id"""
                     ),
                     desc =
                         s"Issues ${y("'model/sugsyn'")} REST call with default min score and given model ID."
                 ),
                 Example(
                     usage = Seq(
-                        s"""> sugsyn"""
+                        s"""> model-sugsyn"""
                     ),
                     desc =
                         s"Issues ${y("'model/sugsyn'")} REST call with default min score and default model ID " +
@@ -659,7 +731,7 @@
             synopsis = s"Starts local server.",
             desc = Some(
                 s"Server is started in the external JVM process with both stdout and stderr piped out into log file. " +
-                s"Command will block until the server is started unless ${c("'--noWait'")} parameter is used or timeout is expired."
+                s"Command will block until the server is started unless ${y("'--noWait'")} parameter is used or timeout is expired."
             ),
             body = NCCli.cmdStartServer,
             params = Seq(
@@ -732,7 +804,7 @@
             synopsis = s"Starts local probe.",
             desc = Some(
                 s"Probe is started in the external JVM process with both stdout and stderr piped out into log file. " +
-                s"Command will block until the probe is started unless ${c("'--noWait'")} parameter is used or timeout is expired."
+                s"Command will block until the probe is started unless ${y("'--noWait'")} parameter is used or timeout is expired."
             ),
             body = NCCli.cmdStartProbe,
             params = Seq(
@@ -746,7 +818,7 @@
                         s"When starting a probe with your models you must " +
                         s"provide this additional classpath for the models and their dependencies this probe will be hosting. " +
                         s"Parameter should include one or more classpath entry (JAR or directory) separated by the OS specific classpath separator. " +
-                        s"Note that you can have multiple ${c("'--cp'")} parameters and their values will be " +
+                        s"Note that you can have multiple ${y("'--cp'")} parameters and their values will be " +
                         s"automatically combined into one final additional classpath. " +
                         s"Note also that you can use ${y("'~'")} at the beginning of the classpath component to specify user home directory."
                 ),
@@ -771,8 +843,8 @@
                     desc =
                         s"Comma separated list of fully qualified class names for models to deploy. This will override " +
                         s"${y("'nlpcraft.probe.models'")} configuration property from either default configuration file " +
-                        s"or the one provided by ${c("'--cfg'")} parameter. Note that you also must provide the additional " +
-                        s"classpath in this case via ${c("'--cp'")} parameter. Note also that you can have multiple '${c("'--mdls'")} " +
+                        s"or the one provided by ${y("'--cfg'")} parameter. Note that you also must provide the additional " +
+                        s"classpath in this case via ${y("'--cp'")} parameter. Note also that you can have multiple '${y("'--mdls'")} " +
                         s"parameters - each specifying one or more model class names - and they will be automatically combined together."
                 ),
                 Parameter(
@@ -826,7 +898,7 @@
             synopsis = s"Restarts local probe (REPL mode only).",
             desc = Some(
                 s"Restart local probe with same parameters as the last start. Works only in REPL mode and only if local " +
-                s"probe was previously started using ${c("'start-probe")} command. This command provides a " +
+                s"probe was previously started using ${y("'start-probe")} command. This command provides a " +
                 s"convenient way to quickly restart the probe to reload the model during model development and testing."
             ),
             body = NCCli.cmdRestartProbe,
@@ -860,7 +932,7 @@
                         s"Additional JVM classpath that will be appended to the default NLPCraft JVM classpath. " +
                         s"When testing your models you must provide this additional classpath for the models and their dependencies. " +
                         s"Parameter should include one or more classpath entry (JAR or directory) separated by the OS specific classpath separator. " +
-                        s"Note that you can have multiple ${c("'--cp'")} parameters and their values will be " +
+                        s"Note that you can have multiple ${y("'--cp'")} parameters and their values will be " +
                         s"automatically combined into one final additional classpath. " +
                         s"Note also that you can use ${y("'~'")} at the beginning of the classpath component to specify user home directory."
                 ),
@@ -883,9 +955,9 @@
                     optional = true,
                     desc =
                         s"Comma separated list of fully qualified class names for models to deploy and test. Note that you also " +
-                        s"must provide the additional classpath via ${c("'--cp'")} parameter. If not provided, the models " +
-                        s"specified in configuration file (${c("'--cfg'")} parameter) will be used instead. Note that " +
-                        s"you can have multiple ${c("'--mdls'")} parameters - each specifying one or more model class " +
+                        s"must provide the additional classpath via ${y("'--cp'")} parameter. If not provided, the models " +
+                        s"specified in configuration file (${y("'--cfg'")} parameter) will be used instead. Note that " +
+                        s"you can have multiple ${y("'--mdls'")} parameters - each specifying one or more model class " +
                         s"names - and they will be automatically combined together."
                 ),
                 Parameter(
@@ -1074,7 +1146,7 @@
             group = "3. Miscellaneous",
             synopsis = s"Displays help for ${y(s"'$SCRIPT_NAME'")}.",
             desc = Some(
-                s"By default, without ${c("'--all'")} or ${c("'--cmd'")} parameters, displays the abbreviated form of manual " +
+                s"By default, without ${y("'--all'")} or ${y("'--cmd'")} parameters, displays the abbreviated form of manual " +
                 s"only listing the commands without parameters or examples."
             ),
             body = NCCli.cmdHelp,
diff --git a/nlpcraft/src/main/scala/org/apache/nlpcraft/model/tools/cmdline/NCCliRestSpec.scala b/nlpcraft/src/main/scala/org/apache/nlpcraft/model/tools/cmdline/NCCliRestSpec.scala
index 4c23223..7bbdc61 100644
--- a/nlpcraft/src/main/scala/org/apache/nlpcraft/model/tools/cmdline/NCCliRestSpec.scala
+++ b/nlpcraft/src/main/scala/org/apache/nlpcraft/model/tools/cmdline/NCCliRestSpec.scala
@@ -78,6 +78,25 @@
             )
         ),
         RestSpec(
+            "model/syns",
+            "Gets model element synonyms",
+            "Tools",
+            params = Seq(
+                RestSpecParameter(name = "acsTok", kind = STRING),
+                RestSpecParameter(name = "mdlId", kind = STRING),
+                RestSpecParameter(name = "elmId", kind = STRING)
+            )
+        ),
+        RestSpec(
+            "model/info",
+            "Gets model configuration and elements",
+            "Tools",
+            params = Seq(
+                RestSpecParameter(name = "acsTok", kind = STRING),
+                RestSpecParameter(name = "mdlId", kind = STRING)
+            )
+        ),
+        RestSpec(
             "check",
             "Gets status and result of submitted requests",
             "Asking",
diff --git a/nlpcraft/src/main/scala/org/apache/nlpcraft/probe/mgrs/cmd/NCCommandManager.scala b/nlpcraft/src/main/scala/org/apache/nlpcraft/probe/mgrs/cmd/NCCommandManager.scala
index 1f96350..4c89d54 100644
--- a/nlpcraft/src/main/scala/org/apache/nlpcraft/probe/mgrs/cmd/NCCommandManager.scala
+++ b/nlpcraft/src/main/scala/org/apache/nlpcraft/probe/mgrs/cmd/NCCommandManager.scala
@@ -17,12 +17,12 @@
 
 package org.apache.nlpcraft.probe.mgrs.cmd
 
-import java.io.{Serializable => JSerializable}
-import com.google.gson.Gson
+import com.fasterxml.jackson.databind.ObjectMapper
+import com.fasterxml.jackson.module.scala.DefaultScalaModule
 import io.opencensus.trace.Span
-import org.apache.nlpcraft.common.{NCService, _}
 import org.apache.nlpcraft.common.nlp.NCNlpSentence
-import org.apache.nlpcraft.model.NCToken
+import org.apache.nlpcraft.common.{NCService, _}
+import org.apache.nlpcraft.model.{NCCustomParser, NCElement, NCModelView, NCToken, NCValue, NCValueLoader}
 import org.apache.nlpcraft.probe.mgrs.NCProbeMessage
 import org.apache.nlpcraft.probe.mgrs.conn.NCConnectionManager
 import org.apache.nlpcraft.probe.mgrs.conversation.NCConversationManager
@@ -30,22 +30,19 @@
 import org.apache.nlpcraft.probe.mgrs.model.NCModelManager
 import org.apache.nlpcraft.probe.mgrs.nlp.NCProbeEnrichmentManager
 
-import java.util
-import java.util.{List => JList}
-
-import scala.jdk.CollectionConverters.{ListHasAsScala, MapHasAsJava, MapHasAsScala, SeqHasAsJava, SetHasAsScala}
+import java.io.{Serializable => JSerializable}
+import java.util.{Collections, Optional, List => JList}
+import java.{lang, util}
+import scala.jdk.CollectionConverters.{ListHasAsScala, MapHasAsJava, MapHasAsScala, SeqHasAsJava, SetHasAsJava, SetHasAsScala}
 
 /**
   * Probe commands processor.
   */
 object NCCommandManager extends NCService {
-    private final val GSON = new Gson()
+    private final val JS_MAPPER = new ObjectMapper()
 
-    /**
-     * Starts this service.
-     *
-     * @param parent Optional parent span.
-     */
+    JS_MAPPER.registerModule(DefaultScalaModule)
+
     override def start(parent: Span): NCService = startScopedSpan("start", parent) { _ =>
         ackStarting()
         ackStarted()
@@ -63,6 +60,28 @@
 
     /**
       *
+      * @param mkMsg
+      * @param mkErrorMsg
+      */
+    private def send0(mkMsg: () => NCProbeMessage, mkErrorMsg: Throwable => NCProbeMessage, parent: Span = null): Unit = {
+        val msgOpt: Option[NCProbeMessage] =
+            try
+                Some(mkMsg())
+            catch {
+                case e: Throwable =>
+                    NCConnectionManager.send(mkErrorMsg(e), parent)
+
+                    None
+            }
+
+        msgOpt match {
+            case Some(msg) => NCConnectionManager.send(msg, parent)
+            case None => // No-op.
+        }
+    }
+
+    /**
+      *
       * @param msg Server message to process.
       * @param parent Optional parent span.
       */
@@ -107,27 +126,172 @@
                             span
                     )
 
-                    case "S2P_MODEL_INFO" =>
-                        val mdlId = msg.data[String]("mdlId")
+                    case "S2P_MODEL_SYNS_INFO" =>
+                        send0(
+                            mkMsg = () => {
+                                val mdlId = msg.data[String]("mdlId")
 
-                        val mdlData = NCModelManager.getModel(mdlId)
+                                val mdlData = NCModelManager.getModel(mdlId)
 
-                        val macros: util.Map[String, String] = mdlData.model.getMacros
-                        val syns: util.Map[String, util.List[String]] = mdlData.model.getElements.asScala.map(p => p.getId -> p.getSynonyms).toMap.asJava
-                        val samples: util.Map[String, util.List[util.List[String]]] = mdlData.samples.map(p => p._1 -> p._2.map(_.asJava).asJava).toMap.asJava
+                                val macros: util.Map[String, String] = mdlData.model.getMacros
+                                val syns: util.Map[String, util.List[String]] =
+                                    mdlData.model.getElements.asScala.map(p => p.getId -> p.getSynonyms).toMap.asJava
+                                val samples: util.Map[String, util.List[util.List[String]]] =
+                                    mdlData.samples.map(p => p._1 -> p._2.map(_.asJava).asJava).toMap.asJava
 
-                        NCConnectionManager.send(
-                            NCProbeMessage(
-                                "P2S_MODEL_INFO",
-                                "reqGuid" -> msg.getGuid,
-                                "resp" -> GSON.toJson(
-                                    Map(
-                                        "macros" -> macros.asInstanceOf[JSerializable],
-                                        "synonyms" -> syns.asInstanceOf[JSerializable],
-                                        "samples" -> samples.asInstanceOf[JSerializable]
-                                    ).asJava
+                                NCProbeMessage(
+                                    "P2S_MODEL_SYNS_INFO",
+                                    "reqGuid" -> msg.getGuid,
+                                    "resp" -> JS_MAPPER.writeValueAsString(
+                                        Map(
+                                            "macros" -> macros.asInstanceOf[JSerializable],
+                                            "synonyms" -> syns.asInstanceOf[JSerializable],
+                                            "samples" -> samples.asInstanceOf[JSerializable]
+                                        ).asJava
+                                    )
                                 )
-                            ),
+                            },
+                            mkErrorMsg = e =>
+                                NCProbeMessage(
+                                    "P2S_MODEL_SYNS_INFO",
+                                    "reqGuid" -> msg.getGuid,
+                                    "error" -> e.getLocalizedMessage
+                                ),
+                            span
+                        )
+
+                    case "S2P_MODEL_ELEMENT_INFO" =>
+                        send0(
+                            mkMsg = () => {
+                                val mdlId = msg.data[String]("mdlId")
+                                val elmId = msg.data[String]("elmId")
+
+                                val mdl = NCModelManager.getModel(mdlId).model
+
+                                val elm = mdl.getElements.asScala.find(_.getId == elmId).
+                                    getOrElse(throw new NCE(s"Element not found in model: $elmId"))
+
+                                val vals: util.Map[String, JList[String]] =
+                                    if (elm.getValues != null)
+                                        elm.getValues.asScala.map(e => e.getName -> e.getSynonyms).toMap.asJava
+                                    else
+                                        Collections.emptyMap()
+
+                                NCProbeMessage(
+                                    "P2S_MODEL_ELEMENT_INFO",
+                                    "reqGuid" -> msg.getGuid,
+                                    "resp" -> JS_MAPPER.writeValueAsString(
+                                        Map(
+                                            "synonyms" -> elm.getSynonyms.asInstanceOf[JSerializable],
+                                            "values" -> vals.asInstanceOf[JSerializable],
+                                            "macros" -> mdl.getMacros.asInstanceOf[JSerializable]
+                                        ).asJava
+                                    )
+                                )
+                            },
+                            mkErrorMsg = e =>
+                                NCProbeMessage(
+                                    "P2S_MODEL_ELEMENT_INFO",
+                                    "reqGuid" -> msg.getGuid,
+                                    "error" -> e.getLocalizedMessage
+                                ),
+                            span
+                        )
+
+                    case "S2P_MODEL_INFO" =>
+                        send0(
+                            mkMsg = () => {
+                                val mdlId = msg.data[String]("mdlId")
+
+                                val mdl = NCModelManager.getModel(mdlId).model
+
+                                val convertedMdl =
+                                    new NCModelView {
+                                        // As is.
+                                        override def getId: String = mdl.getId
+                                        override def getName: String = mdl.getName
+                                        override def getVersion: String = mdl.getVersion
+                                        override def getDescription: String = mdl.getDescription
+                                        override def getOrigin: String = mdl.getOrigin
+                                        override def getMaxUnknownWords: Int = mdl.getMaxUnknownWords
+                                        override def getMaxFreeWords: Int = mdl.getMaxFreeWords
+                                        override def getMaxSuspiciousWords: Int = mdl.getMaxSuspiciousWords
+                                        override def getMinWords: Int = mdl.getMinWords
+                                        override def getMaxWords: Int = mdl.getMaxWords
+                                        override def getMinTokens: Int = mdl.getMinTokens
+                                        override def getMaxTokens: Int = mdl.getMaxTokens
+                                        override def getMinNonStopwords: Int = mdl.getMinNonStopwords
+                                        override def isNonEnglishAllowed: Boolean = mdl.isNonEnglishAllowed
+                                        override def isNotLatinCharsetAllowed: Boolean = mdl.isNotLatinCharsetAllowed
+                                        override def isSwearWordsAllowed: Boolean = mdl.isSwearWordsAllowed
+                                        override def isNoNounsAllowed: Boolean = mdl.isNoNounsAllowed
+                                        override def isPermutateSynonyms: Boolean = mdl.isPermutateSynonyms
+                                        override def isDupSynonymsAllowed: Boolean = mdl.isDupSynonymsAllowed
+                                        override def getMaxTotalSynonyms: Int = mdl.getMaxTotalSynonyms
+                                        override def isNoUserTokensAllowed: Boolean = mdl.isNoUserTokensAllowed
+                                        override def isSparse: Boolean = mdl.isSparse
+                                        override def getMetadata: util.Map[String, AnyRef] = mdl.getMetadata
+                                        override def getAdditionalStopWords: util.Set[String] = mdl.getAdditionalStopWords
+                                        override def getExcludedStopWords: util.Set[String] = mdl.getExcludedStopWords
+                                        override def getSuspiciousWords: util.Set[String] = mdl.getSuspiciousWords
+                                        override def getMacros: util.Map[String, String] = mdl.getMacros
+                                        override def getEnabledBuiltInTokens: util.Set[String] = mdl.getEnabledBuiltInTokens
+                                        override def getAbstractTokens: util.Set[String] = mdl.getAbstractTokens
+                                        override def getMaxElementSynonyms: Int = mdl.getMaxElementSynonyms
+                                        override def isMaxSynonymsThresholdError: Boolean = mdl.isMaxSynonymsThresholdError
+                                        override def getConversationTimeout: Long = mdl.getConversationTimeout
+                                        override def getConversationDepth: Int = mdl.getConversationDepth
+                                        override def getRestrictedCombinations: util.Map[String, util.Set[String]] = mdl.getRestrictedCombinations
+
+                                        // Cleared.
+                                        override def getParsers: JList[NCCustomParser] = null
+
+                                        // Add list of just element IDs.
+                                        val elementIds: JList[String] = mdl.getElements.asScala.map(_.getId).toList.asJava
+
+                                        // Converted.
+                                        override def getElements: util.Set[NCElement] = mdl.getElements.asScala.map(e =>
+                                            new NCElement {
+                                                // As is.
+                                                override def getId: String = e.getId
+                                                override def getGroups: JList[String] = e.getGroups
+                                                override def getMetadata: util.Map[String, AnyRef] = e.getMetadata
+                                                override def getDescription: String = e.getDescription
+                                                override def getParentId: String = e.getParentId
+                                                override def getSynonyms: JList[String] = e.getSynonyms
+                                                override def isPermutateSynonyms: Optional[lang.Boolean] = e.isPermutateSynonyms
+                                                override def isSparse: Optional[lang.Boolean] = e.isSparse
+
+                                                // Cleared.
+                                                override def getValueLoader: Optional[NCValueLoader] = null
+
+                                                // Converted.
+                                                override def getValues: JList[NCValue] =
+                                                    if (e.getValues != null) {
+                                                        e.getValues.asScala.map(v => new NCValue {
+                                                            override def getName: String = v.getName
+                                                            // Cleared.
+                                                            override def getSynonyms: JList[String] = null
+                                                        }).asJava
+                                                    }
+                                                    else
+                                                        null
+                                            }
+                                        ).asJava
+                                    }
+
+                                NCProbeMessage(
+                                    "P2S_MODEL_INFO",
+                                    "reqGuid" -> msg.getGuid,
+                                    "resp" -> JS_MAPPER.writeValueAsString(convertedMdl)
+                                )
+                            },
+                            mkErrorMsg = e =>
+                                NCProbeMessage(
+                                    "P2S_MODEL_SYNS_INFO",
+                                    "reqGuid" -> msg.getGuid,
+                                    "error" -> e.getLocalizedMessage
+                                ),
                             span
                         )
 
diff --git a/nlpcraft/src/main/scala/org/apache/nlpcraft/probe/mgrs/conn/NCConnectionManager.scala b/nlpcraft/src/main/scala/org/apache/nlpcraft/probe/mgrs/conn/NCConnectionManager.scala
index 362e930..d2a4619 100644
--- a/nlpcraft/src/main/scala/org/apache/nlpcraft/probe/mgrs/conn/NCConnectionManager.scala
+++ b/nlpcraft/src/main/scala/org/apache/nlpcraft/probe/mgrs/conn/NCConnectionManager.scala
@@ -34,6 +34,7 @@
 import java.util.concurrent.CountDownLatch
 import java.util.{Properties, TimeZone}
 import scala.collection.mutable
+import scala.jdk.CollectionConverters.{SetHasAsJava, SetHasAsScala}
 
 /**
   * Probe down/up link connection manager.
@@ -221,7 +222,8 @@
                                 mdl.getId,
                                 mdl.getName,
                                 mdl.getVersion,
-                                new util.HashSet[String](mdl.getEnabledBuiltInTokens)
+                                new util.HashSet[String](mdl.getEnabledBuiltInTokens),
+                                new util.HashSet[String](mdl.getElements.asScala.map(_.getId).asJava)
                             )
                         })
                 ), cryptoKey)
diff --git a/nlpcraft/src/main/scala/org/apache/nlpcraft/probe/mgrs/deploy/NCDeployManager.scala b/nlpcraft/src/main/scala/org/apache/nlpcraft/probe/mgrs/deploy/NCDeployManager.scala
index a72b6c0..028608a 100644
--- a/nlpcraft/src/main/scala/org/apache/nlpcraft/probe/mgrs/deploy/NCDeployManager.scala
+++ b/nlpcraft/src/main/scala/org/apache/nlpcraft/probe/mgrs/deploy/NCDeployManager.scala
@@ -923,16 +923,18 @@
         checkCollection("metadata", mdl.getMetadata)
         checkCollection("restrictedCombinations", mdl.getRestrictedCombinations)
 
+        mdl.getElements.asScala.foreach(e => checkMandatoryString(e.getId,"element.id", MODEL_ELEMENT_ID_MAXLEN))
+
         for ((elm, restrs: util.Set[String]) <- mdl.getRestrictedCombinations.asScala) {
             if (elm != "nlpcraft:limit" && elm != "nlpcraft:sort" && elm != "nlpcraft:relation")
-                throw new NCE(s"Unsupported restricting element ID [" +
+                throw new NCE(s"Unsupported restricting element [" +
                     s"mdlId=$mdlId, " +
-                    s"elemId=$elm" +
+                    s"elmId=$elm" +
                 s"]. Only 'nlpcraft:limit', 'nlpcraft:sort', and 'nlpcraft:relation' are allowed.")
             if (restrs.contains(elm))
                 throw new NCE(s"Element cannot be restricted to itself [" +
                     s"mdlId=$mdlId, " +
-                    s"elemId=$elm" +
+                    s"elmId=$elm" +
                 s"]")
         }
 
diff --git a/nlpcraft/src/main/scala/org/apache/nlpcraft/probe/mgrs/nlp/enrichers/model/NCModelEnricher.scala b/nlpcraft/src/main/scala/org/apache/nlpcraft/probe/mgrs/nlp/enrichers/model/NCModelEnricher.scala
index a2deee8..1f81711 100644
--- a/nlpcraft/src/main/scala/org/apache/nlpcraft/probe/mgrs/nlp/enrichers/model/NCModelEnricher.scala
+++ b/nlpcraft/src/main/scala/org/apache/nlpcraft/probe/mgrs/nlp/enrichers/model/NCModelEnricher.scala
@@ -250,14 +250,14 @@
 
                 if (res != null)
                     res.asScala.foreach(e => {
-                        val elemId = e.getElementId
+                        val elmId = e.getElementId
                         val words = e.getWords
 
-                        if (elemId == null)
+                        if (elmId == null)
                             throw new NCE(s"Custom model parser cannot return 'null' element ID.")
 
                         if (words == null || words.isEmpty)
-                            throw new NCE(s"Custom model parser cannot return empty custom tokens [elementId=$elemId]")
+                            throw new NCE(s"Custom model parser cannot return empty custom tokens for element: $elmId")
 
                         val matchedToks = words.asScala.map(w =>
                             ns.find(t =>
@@ -266,10 +266,10 @@
                         )
 
                         // Checks element's tokens.
-                        if (!alreadyMarked(ns, elemId, matchedToks, matchedToks.map(_.index).sorted))
+                        if (!alreadyMarked(ns, elmId, matchedToks, matchedToks.map(_.index).sorted))
                             mark(
                                 ns,
-                                elem = mdl.elements.getOrElse(elemId, throw new NCE(s"Custom model parser returned unknown element ID: $elemId")),
+                                elem = mdl.elements.getOrElse(elmId, throw new NCE(s"Custom model parser returned unknown element: $elmId")),
                                 toks = matchedToks,
                                 direct = true,
                                 metaOpt = Some(e.getMetadata.asScala.toMap)
@@ -577,16 +577,16 @@
     // TODO: simplify, add tests, check model properties (sparse etc) for optimization.
     /**
       *
-      * @param elemId
+      * @param elmId
       * @param toks
       * @param sliceToksIdxsSorted
       */
-    private def alreadyMarked(ns: Sentence, elemId: String, toks: Seq[NlpToken], sliceToksIdxsSorted: Seq[Int]): Boolean = {
+    private def alreadyMarked(ns: Sentence, elmId: String, toks: Seq[NlpToken], sliceToksIdxsSorted: Seq[Int]): Boolean = {
         lazy val toksIdxsSorted = toks.map(_.index).sorted
 
-        sliceToksIdxsSorted.map(ns).forall(_.exists(n => n.noteType == elemId && n.sparsity == 0)) ||
+        sliceToksIdxsSorted.map(ns).forall(_.exists(n => n.noteType == elmId && n.sparsity == 0)) ||
         toks.exists(_.exists(n =>
-            n.noteType == elemId &&
+            n.noteType == elmId &&
             (
                 (n.sparsity == 0 &&
                     (sliceToksIdxsSorted.containsSlice(n.tokenIndexes) || n.tokenIndexes.containsSlice(toksIdxsSorted))
diff --git a/nlpcraft/src/main/scala/org/apache/nlpcraft/server/mdo/NCProbeModelMdo.scala b/nlpcraft/src/main/scala/org/apache/nlpcraft/server/mdo/NCProbeModelMdo.scala
index 16edd61..1b6001b 100644
--- a/nlpcraft/src/main/scala/org/apache/nlpcraft/server/mdo/NCProbeModelMdo.scala
+++ b/nlpcraft/src/main/scala/org/apache/nlpcraft/server/mdo/NCProbeModelMdo.scala
@@ -27,7 +27,8 @@
     @NCMdoField id: String,
     @NCMdoField name: String,
     @NCMdoField version: String,
-    @NCMdoField enabledBuiltInTokens: Set[String]
+    @NCMdoField enabledBuiltInTokens: Set[String],
+    @NCMdoField elementIds: Set[String]
 ) extends NCAnnotatedMdo[NCProbeModelMdo] {
     override def hashCode(): Int = s"$id$name".hashCode()
     
diff --git a/nlpcraft/src/main/scala/org/apache/nlpcraft/server/opencensus/NCOpenCensusServerStats.scala b/nlpcraft/src/main/scala/org/apache/nlpcraft/server/opencensus/NCOpenCensusServerStats.scala
index 25340d7..e826dda 100644
--- a/nlpcraft/src/main/scala/org/apache/nlpcraft/server/opencensus/NCOpenCensusServerStats.scala
+++ b/nlpcraft/src/main/scala/org/apache/nlpcraft/server/opencensus/NCOpenCensusServerStats.scala
@@ -17,9 +17,7 @@
 
 package org.apache.nlpcraft.server.opencensus
 
-import java.util.Collections
 import io.opencensus.stats.Measure._
-import io.opencensus.stats.View.Name
 import io.opencensus.stats._
 import org.apache.nlpcraft.common.opencensus.NCOpenCensusStats
 
@@ -30,7 +28,8 @@
     val M_HEALTH_MS: MeasureLong = MeasureLong.create("health_latency", "The latency of '/health' REST call", "ms")
     val M_ASK_LATENCY_MS: MeasureLong = MeasureLong.create("ask_latency", "The latency of '/ask' REST call", "ms")
     val M_CHECK_LATENCY_MS: MeasureLong = MeasureLong.create("check_latency", "The latency of '/check' REST call", "ms")
-    val M_MODEL_SUGSYN_LATENCY_MS: MeasureLong = MeasureLong.create("model_inspect_latency", "The latency of '/model/inspect' REST call", "ms")
+    val M_MODEL_SUGSYN_LATENCY_MS: MeasureLong = MeasureLong.create("model_inspect_latency", "The latency of '/model/sugsyn' REST call", "ms")
+    val M_MODEL_SYNS_LATENCY_MS: MeasureLong = MeasureLong.create("model_synonyms_latency", "The latency of '/model/syns' REST call", "ms")
     val M_CANCEL_LATENCY_MS: MeasureLong = MeasureLong.create("cancel_latency", "The latency of '/cancel' REST call", "ms")
     val M_SIGNIN_LATENCY_MS: MeasureLong = MeasureLong.create("signin_latency", "The latency of '/signin' REST call", "ms")
     val M_SIGNOUT_LATENCY_MS: MeasureLong = MeasureLong.create("signout_latency", "The latency of '/signout' REST call", "ms")
@@ -77,6 +76,7 @@
             mkViews(M_COMPANY_TOKEN_LATENCY_MS, "company/token"),
             mkViews(M_COMPANY_DELETE_LATENCY_MS, "company/delete"),
             mkViews(M_MODEL_SUGSYN_LATENCY_MS, "model/sugsyn"),
+            mkViews(M_MODEL_SYNS_LATENCY_MS, "model/syns"),
             mkViews(M_USER_ADD_LATENCY_MS, "user/add"),
             mkViews(M_USER_GET_LATENCY_MS, "user/get"),
             mkViews(M_USER_DELETE_LATENCY_MS, "user/delete"),
@@ -87,7 +87,6 @@
             mkViews(M_FEEDBACK_ADD_LATENCY_MS, "feedback/add"),
             mkViews(M_FEEDBACK_DELETE_LATENCY_MS, "feedback/delete"),
             mkViews(M_FEEDBACK_GET_LATENCY_MS, "feedback/get"),
-            mkViews(M_MODEL_SUGSYN_LATENCY_MS, "model/sugsyn"),
             mkViews(M_PROBE_ALL_LATENCY_MS, "probe/all"),
             mkViews(M_ROUND_TRIP_LATENCY_MS, "roundTrip/latdist"),
         ).flatten
diff --git a/nlpcraft/src/main/scala/org/apache/nlpcraft/server/probe/NCProbeManager.scala b/nlpcraft/src/main/scala/org/apache/nlpcraft/server/probe/NCProbeManager.scala
index f572b9f..feed48c 100644
--- a/nlpcraft/src/main/scala/org/apache/nlpcraft/server/probe/NCProbeManager.scala
+++ b/nlpcraft/src/main/scala/org/apache/nlpcraft/server/probe/NCProbeManager.scala
@@ -23,6 +23,7 @@
 import org.apache.nlpcraft.common.ascii.NCAsciiTable
 import org.apache.nlpcraft.common.config.NCConfigurable
 import org.apache.nlpcraft.common.crypto.NCCipher
+import org.apache.nlpcraft.common.makro.NCMacroParser
 import org.apache.nlpcraft.common.nlp.NCNlpSentence
 import org.apache.nlpcraft.common.nlp.core.NCNlpCoreManager
 import org.apache.nlpcraft.common.pool.NCThreadPoolManager
@@ -45,7 +46,7 @@
 import java.util.concurrent.ConcurrentHashMap
 import scala.collection.mutable
 import scala.concurrent.{ExecutionContext, Future, Promise}
-import scala.jdk.CollectionConverters.SetHasAsScala
+import scala.jdk.CollectionConverters.{ListHasAsScala, MapHasAsJava, MapHasAsScala, SeqHasAsJava, SetHasAsScala}
 import scala.util.{Failure, Success}
 
 /**
@@ -53,7 +54,7 @@
   */
 object NCProbeManager extends NCService {
     private final val GSON = new Gson()
-    private final val TYPE_MODEL_INFO_RESP = new TypeToken[JavaMeta]() {}.getType
+    private final val TYPE_JAVA_META = new TypeToken[JavaMeta]() {}.getType
 
     // Type safe and eager configuration container.
     private object Config extends NCConfigurable {
@@ -65,7 +66,7 @@
         def reconnectTimeoutMs: Long = getLong(s"$pre.reconnectTimeoutMs")
         def pingTimeoutMs: Long = getLong(s"$pre.pingTimeoutMs")
         def soTimeoutMs: Int = getInt(s"$pre.soTimeoutMs")
-    
+
         /**
           *
           */
@@ -156,6 +157,8 @@
     // All probes pending complete handshake keyed by probe key.
     @volatile private var pending: mutable.Map[ProbeKey, ProbeHolder] = _
 
+    @volatile private var modelsSynsInfo: ConcurrentHashMap[String, Promise[JavaMeta]] = _
+    @volatile private var modelElmsInfo: ConcurrentHashMap[String, Promise[JavaMeta]] = _
     @volatile private var modelsInfo: ConcurrentHashMap[String, Promise[JavaMeta]] = _
 
     /**
@@ -178,6 +181,8 @@
             "downlink" -> s"$dnHost:$dnPort"
         )
 
+        modelsSynsInfo = new ConcurrentHashMap[String, Promise[JavaMeta]]()
+        modelElmsInfo = new ConcurrentHashMap[String, Promise[JavaMeta]]()
         modelsInfo = new ConcurrentHashMap[String, Promise[JavaMeta]]()
 
         dnSrv = startServer("Downlink", dnHost, dnPort, downLinkHandler)
@@ -215,6 +220,8 @@
         U.stopThread(dnSrv)
         U.stopThread(upSrv)
 
+        modelsSynsInfo = null
+        modelElmsInfo = null
         modelsInfo = null
      
         ackStopped()
@@ -613,6 +620,7 @@
                             String,
                             String,
                             String,
+                            java.util.Set[String],
                             java.util.Set[String]
                         )]]("PROBE_MODELS").
                         map {
@@ -620,18 +628,21 @@
                                 mdlId,
                                 mdlName,
                                 mdlVer,
-                                enabledBuiltInToks
+                                enabledBuiltInToks,
+                                elmIds
                             ) =>
                                 require(mdlId != null)
                                 require(mdlName != null)
                                 require(mdlVer != null)
                                 require(enabledBuiltInToks != null)
+                                require(elmIds != null)
 
                                 NCProbeModelMdo(
                                     id = mdlId,
                                     name = mdlName,
                                     version = mdlVer,
-                                    enabledBuiltInTokens = enabledBuiltInToks.asScala.toSet
+                                    enabledBuiltInTokens = enabledBuiltInToks.asScala.toSet,
+                                    elementIds = elmIds.asScala.toSet
                                 )
                         }.toSet
 
@@ -682,6 +693,23 @@
             }
         }
     }
+
+    /**
+      *
+      * @param probeMsg
+      * @param m
+      */
+    private def processJavaMetaMessage(probeMsg: NCProbeMessage, m: ConcurrentHashMap[String, Promise[JavaMeta]]): Unit = {
+        val p = m.remove(probeMsg.data[String]("reqGuid"))
+
+        if (p != null)
+            probeMsg.dataOpt[String]("resp") match {
+                case Some(resp) => p.success(GSON.fromJson(resp, TYPE_JAVA_META))
+                case None => p.failure(new NCE(probeMsg.data[String]("error")))
+            }
+        else
+            logger.warn(s"Message ignored: $probeMsg")
+    }
     
     /**
       * Processes the messages received from the probe.
@@ -707,13 +735,10 @@
             typ match {
                 case "P2S_PING" => ()
 
-                case "P2S_MODEL_INFO" =>
-                    val p = modelsInfo.remove(probeMsg.data[String]("reqGuid"))
+                case "P2S_MODEL_SYNS_INFO" => processJavaMetaMessage(probeMsg, modelsSynsInfo)
+                case "P2S_MODEL_ELEMENT_INFO" => processJavaMetaMessage(probeMsg, modelElmsInfo)
+                case "P2S_MODEL_INFO" => processJavaMetaMessage(probeMsg, modelsInfo)
 
-                    if (p != null)
-                        p.success(GSON.fromJson(probeMsg.data[String]("resp"), TYPE_MODEL_INFO_RESP))
-                    else
-                        logger.warn(s"Message ignored: $probeMsg")
                 case "P2S_ASK_RESULT" =>
                     val srvReqId = probeMsg.data[String]("srvReqId")
                     
@@ -763,6 +788,7 @@
                                 NCErrorCodes.UNEXPECTED_ERROR
                             )
                     }
+
                 case _ =>
                     logger.error(s"Received unrecognized probe message (ignoring): $probeMsg")
             }
@@ -966,6 +992,27 @@
         }
 
     /**
+      * Checks whether or not a data probe exists for given model element.
+      *
+      * @param compId Company ID for authentication purpose.
+      * @param mdlId Model ID.
+      * @param elmId Element ID.
+      * @param parent Optional parent span.
+      * @return
+      */
+    def existsForModelElement(compId: Long, mdlId: String, elmId: String, parent: Span = null): Boolean =
+        startScopedSpan(
+            "existsForModelElement", parent, "compId" -> compId, "mdlId" -> mdlId, "elmId" -> elmId
+        ) { _ =>
+            val authTok = getCompany(compId).authToken
+
+            probes.synchronized {
+                probes.filter(_._1.probeToken == authTok).values.
+                    exists(_.probe.models.exists(p => p.id == mdlId && p.elementIds.contains(elmId)))
+            }
+        }
+
+    /**
       *
       * @param usrId User ID.
       * @param mdlId Model ID.
@@ -1016,24 +1063,95 @@
     /**
       *
       * @param mdlId
+      * @param msg
+      * @param holder
+      * @param parent
+      */
+    private def processModelDataRequest(
+        mdlId: String, msg: NCProbeMessage, holder: ConcurrentHashMap[String, Promise[JavaMeta]], parent: Span = null
+    ): Future[JavaMeta] = {
+        val p = Promise[JavaMeta]()
+
+        getProbeForModelId(mdlId) match {
+            case Some(probe) =>
+                holder.put(msg.getGuid, p)
+
+                sendToProbe(probe.probeKey, msg, parent)
+            case None =>
+                p.failure(new NCE(s"Probe not found for model: '$mdlId''"))
+        }
+
+        p.future
+    }
+
+    /**
+      *
+      * @param mdlId
       * @param parent
       * @return
       */
+    def getModelSynonymsInfo(mdlId: String, parent: Span = null): Future[JavaMeta] =
+        startScopedSpan("getModelSynonymsInfo", parent, "mdlId" -> mdlId) { _ =>
+            processModelDataRequest(
+                mdlId,
+                NCProbeMessage("S2P_MODEL_SYNS_INFO", "mdlId" -> mdlId),
+                modelsSynsInfo,
+                parent
+            )
+        }
+
+    /**
+      *
+      * @param mdlId
+      * @param elmId
+      * @param parent
+      * @return
+      */
+    def getModelElementInfo(mdlId: String, elmId: String, parent: Span = null): Future[JavaMeta] =
+        startScopedSpan("getModelElementInfo", parent, "mdlId" -> mdlId, "elmId" -> elmId) { _ =>
+            processModelDataRequest(
+                mdlId,
+                NCProbeMessage("S2P_MODEL_ELEMENT_INFO", "mdlId" -> mdlId, "elmId" -> elmId),
+                modelElmsInfo,
+                parent
+            ).map(
+                res => {
+                    require(
+                        res.containsKey("synonyms") &&
+                        res.containsKey("values") &&
+                        res.containsKey("macros")
+                    )
+
+                    val macros = res.remove("macros").asInstanceOf[java.util.Map[String, String]].asScala
+                    val syns = res.get("synonyms").asInstanceOf[java.util.List[String]].asScala
+                    val vals = res.get("values").asInstanceOf[java.util.Map[String, java.util.List[String]]].asScala
+
+                    val parser = NCMacroParser(macros.toList)
+
+                    val synsExp = syns.flatMap(s => parser.expand(s)).sorted
+                    val valsExp = vals.map(v => v._1 -> v._2.asScala.flatMap(s => parser.expand(s)).sorted.asJava).toMap
+
+                    res.put("synonymsExp", synsExp.asJava)
+                    res.put("valuesExp", valsExp.asJava)
+
+                    // Add statistics.
+                    res.put("synonymsExpCnt", Integer.valueOf(synsExp.size))
+                    res.put("synonymsCnt", Integer.valueOf(syns.size))
+                    res.put("synonymsExpRatePct", java.lang.Long.valueOf(Math.round(synsExp.size.toDouble * 100 / syns.size.toDouble)))
+
+                    res
+                }
+            )
+        }
+
     def getModelInfo(mdlId: String, parent: Span = null): Future[JavaMeta] =
         startScopedSpan("getModelInfo", parent, "mdlId" -> mdlId) { _ =>
-            getProbeForModelId(mdlId) match {
-                case Some(probe) =>
-                    val msg = NCProbeMessage("S2P_MODEL_INFO", "mdlId" -> mdlId)
-
-                    val p = Promise[JavaMeta]()
-
-                    modelsInfo.put(msg.getGuid, p)
-
-                    sendToProbe(probe.probeKey, msg, parent)
-
-                    p.future
-
-                case None => throw new NCE(s"Probe not found for model: '$mdlId''")
-            }
+            processModelDataRequest(
+                mdlId,
+                NCProbeMessage("S2P_MODEL_INFO", "mdlId" -> mdlId),
+                modelsInfo,
+                parent
+            )
         }
+
 }
diff --git a/nlpcraft/src/main/scala/org/apache/nlpcraft/server/rest/NCBasicRestApi.scala b/nlpcraft/src/main/scala/org/apache/nlpcraft/server/rest/NCBasicRestApi.scala
index 45ab892..8116d07 100644
--- a/nlpcraft/src/main/scala/org/apache/nlpcraft/server/rest/NCBasicRestApi.scala
+++ b/nlpcraft/src/main/scala/org/apache/nlpcraft/server/rest/NCBasicRestApi.scala
@@ -88,6 +88,7 @@
     case class InvalidExternalUserId(usrExtId: String) extends InvalidArguments(s"External user ID is invalid or unknown: $usrExtId")
     case class InvalidUserId(id: Long) extends InvalidArguments(s"User ID is invalid or unknown: $id")
     case class InvalidModelId(id: String) extends InvalidArguments(s"Unknown model ID: $id")
+    case class InvalidModelOrElementId(mdlId: String, elmId: String) extends InvalidArguments(s"Unknown model element ID: $elmId")
 
     case class AskReqHolder(
         usrId: Long,
@@ -106,6 +107,7 @@
     private final val STD_FIELD_LENGTHS = Map[String, Int](
         "srvReqId" -> 32,
         "mdlId" -> NCModelView.MODEL_ID_MAXLEN,
+        "elmId" -> NCModelView.MODEL_ELEMENT_ID_MAXLEN,
         "country" -> 32,
         "postalCode" -> 32,
 
@@ -469,7 +471,7 @@
       *
       * @return
       */
-    protected def signin$(): Route = {
+    protected def $signin(): Route = {
         case class Req$Signin$(
             email: String,
             passwd: String
@@ -504,7 +506,7 @@
       *
       * @return
       */
-    protected def health$(): Route = {
+    protected def $health(): Route = {
         case class Res$Health$(status: String)
 
         implicit val resFmt: RootJsonFormat[Res$Health$] = jsonFormat1(Res$Health$)
@@ -518,7 +520,7 @@
       *
       * @return
       */
-    protected def signout$(): Route = {
+    protected def $signout(): Route = {
         case class Req$Signout$(
             acsTok: String
         )
@@ -632,7 +634,7 @@
       *
       * @return
       */
-    protected def ask$(): Route =
+    protected def $ask(): Route =
         ask0(
             (h: AskReqHolder) => {
                 val newSrvReqId = NCQueryManager.asyncAsk(
@@ -663,7 +665,7 @@
       *
       * @return
       */
-    protected def ask$Sync(): Route =
+    protected def $ask$Sync(): Route =
         ask0(
             (h: AskReqHolder) => {
                 val fut = NCQueryManager.futureAsk(
@@ -695,7 +697,7 @@
       *
       * @return
       */
-    protected def cancel$(): Route = {
+    protected def $cancel(): Route = {
         case class Req$Cancel$(
             acsTok: String,
             usrId: Option[Long],
@@ -734,7 +736,7 @@
       *
       * @return
       */
-    protected def check$(): Route = {
+    protected def $check(): Route = {
         case class Req$Check$(
             acsTok: String,
             usrId: Option[Long],
@@ -785,16 +787,16 @@
       *
       * @return
       */
-    protected def sugsyn$(): Route = {
-        case class Req$Model$Sugsyn$(
+    protected def $model$sugsyn(): Route = {
+        case class Req$Model$Sugsyn(
             acsTok: String,
             mdlId: String,
             minScore: Option[Double]
         )
 
-        implicit val reqFmt: RootJsonFormat[Req$Model$Sugsyn$] = jsonFormat3(Req$Model$Sugsyn$)
+        implicit val reqFmt: RootJsonFormat[Req$Model$Sugsyn] = jsonFormat3(Req$Model$Sugsyn)
 
-        entity(as[Req$Model$Sugsyn$]) { req =>
+        entity(as[Req$Model$Sugsyn]) { req =>
             startScopedSpan(
                 "model$sugsyn",
                 "acsTok" -> req.acsTok,
@@ -819,11 +821,120 @@
         }
     }
 
+
     /**
       *
       * @return
       */
-    protected def clear$Conversation(): Route = {
+    protected def $model$syns(): Route = {
+        case class Req$Model$Syns(
+            acsTok: String,
+            mdlId: String,
+            elmId: String
+        )
+
+        implicit val reqFmt: RootJsonFormat[Req$Model$Syns] = jsonFormat3(Req$Model$Syns)
+
+        entity(as[Req$Model$Syns]) { req =>
+            startScopedSpan(
+                "model$syns",
+                "acsTok" -> req.acsTok,
+                "mdlId" -> req.mdlId,
+                "elmId" -> req.elmId) { span =>
+                checkLength("acsTok" -> req.acsTok, "mdlId" -> req.mdlId, "elmId" -> req.elmId)
+
+                val admUsr = authenticateAsAdmin(req.acsTok)
+                val compId = admUsr.companyId
+
+                if (!NCProbeManager.existsForModel(compId, req.mdlId))
+                    throw InvalidModelId(req.mdlId)
+                if (!NCProbeManager.existsForModelElement(compId, req.mdlId, req.elmId))
+                    throw InvalidModelOrElementId(req.mdlId, req.elmId)
+
+                val fut = NCProbeManager.getModelElementInfo(req.mdlId, req.elmId, span)
+
+                successWithJs(
+                    fut.collect {
+                        // We have to use Jackson (not spray) here to serialize 'result' field.
+                        case res =>
+                            require(
+                                res.containsKey("synonyms") &&
+                                res.containsKey("synonymsExp") &&
+                                res.containsKey("synonymsExpRatePct") &&
+                                res.containsKey("synonymsExpCnt") &&
+                                res.containsKey("synonymsCnt") &&
+                                res.containsKey("values") &&
+                                res.containsKey("valuesExp")
+                            )
+
+                            toJs(
+                                Map(
+                                    "status" -> API_OK.toString,
+                                    "synonymsCnt" -> res.get("synonymsCnt"),
+                                    "synonymsExpCnt" -> res.get("synonymsExpCnt"),
+                                    "synonymsExpRatePct" -> res.get("synonymsExpRatePct"),
+                                    "synonyms" -> res.get("synonyms"),
+                                    "synonymsExp" -> res.get("synonymsExp"),
+                                    "values" -> res.get("values"),
+                                    "valuesExp" -> res.get("valuesExp")
+                                )
+                            )
+                    }
+                )
+            }
+        }
+    }
+
+
+    /**
+      *
+      * @return
+      */
+    protected def $model$info(): Route = {
+        case class Req$Model$Info(
+            acsTok: String,
+            mdlId: String
+        )
+
+        implicit val reqFmt: RootJsonFormat[Req$Model$Info] = jsonFormat2(Req$Model$Info)
+
+        entity(as[Req$Model$Info]) { req =>
+            startScopedSpan(
+                "model$syns",
+                "acsTok" -> req.acsTok,
+                "mdlId" -> req.mdlId
+            ) { span =>
+                checkLength("acsTok" -> req.acsTok, "mdlId" -> req.mdlId)
+
+                val admUsr = authenticateAsAdmin(req.acsTok)
+                val compId = admUsr.companyId
+
+                if (!NCProbeManager.existsForModel(compId, req.mdlId))
+                    throw InvalidModelId(req.mdlId)
+
+                val fut = NCProbeManager.getModelInfo(req.mdlId, span)
+
+                successWithJs(
+                    fut.collect {
+                        // We have to use Jackson (not spray) here to serialize 'result' field.
+                        case res =>
+                            toJs(
+                                Map(
+                                    "status" -> API_OK.toString,
+                                    "model" -> res
+                                )
+                            )
+                    }
+                )
+            }
+        }
+    }
+
+    /**
+      *
+      * @return
+      */
+    protected def $clear$Conversation(): Route = {
         case class Req$Clear$Conversation(
             acsTok: String,
             mdlId: String,
@@ -868,7 +979,7 @@
       *
       * @return
       */
-    protected def clear$Dialog(): Route = {
+    protected def $clear$Dialog(): Route = {
         case class Req$Clear$Dialog(
             acsTok: String,
             mdlId: String,
@@ -913,7 +1024,7 @@
       *
       * @return
       */
-    protected def company$Add(): Route = {
+    protected def $company$Add(): Route = {
         case class Req$Company$Add(
             acsTok: String,
             // New company.
@@ -993,7 +1104,7 @@
       *
       * @return
       */
-    protected def company$Get(): Route = {
+    protected def $company$Get(): Route = {
         case class Req$Company$Get(
             acsTok: String
         )
@@ -1043,7 +1154,7 @@
       *
       * @return
       */
-    protected def company$Update(): Route = {
+    protected def $company$Update(): Route = {
         case class Req$Company$Update(
             // Caller.
             acsTok: String,
@@ -1104,7 +1215,7 @@
       *
       * @return
       */
-    protected def feedback$Add(): Route = {
+    protected def $feedback$Add(): Route = {
         case class Req$Feedback$Add(
             acsTok: String,
             usrId : Option[Long],
@@ -1157,7 +1268,7 @@
       *
       * @return
       */
-    protected def feedback$Delete(): Route = {
+    protected def $feedback$Delete(): Route = {
         case class Req$Feedback$Delete(
             acsTok: String,
             // Feedback IDs to delete (optional).
@@ -1214,7 +1325,7 @@
       *
       * @return
       */
-    protected def feedback$All(): Route = {
+    protected def $feedback$All(): Route = {
         case class Req$Feedback$All(
             acsTok: String,
             usrId: Option[Long],
@@ -1284,7 +1395,7 @@
       *
       * @return
       */
-    protected def company$Token$Reset(): Route = {
+    protected def $company$Token$Reset(): Route = {
         case class Req$Company$Token$Reset(
             // Caller.
             acsTok: String
@@ -1316,7 +1427,7 @@
       *
       * @return
       */
-    protected def company$Delete(): Route = {
+    protected def $company$Delete(): Route = {
         case class Req$Company$Delete(
             // Caller.
             acsTok: String
@@ -1347,7 +1458,7 @@
       *
       * @return
       */
-    protected def user$Add(): Route = {
+    protected def $user$Add(): Route = {
         case class Req$User$Add(
             // Caller.
             acsTok: String,
@@ -1409,7 +1520,7 @@
       *
       * @return
       */
-    protected def user$Update(): Route = {
+    protected def $user$Update(): Route = {
         case class Req$User$Update(
             // Caller.
             acsTok: String,
@@ -1460,7 +1571,7 @@
       *
       * @return
       */
-    protected def user$Delete(): Route = {
+    protected def $user$Delete(): Route = {
         case class Req$User$Delete(
             acsTok: String,
             id: Option[Long],
@@ -1525,7 +1636,7 @@
       *
       * @return
       */
-    protected def user$Admin(): Route = {
+    protected def $user$Admin(): Route = {
         case class Req$User$Admin(
             acsTok: String,
             id: Option[Long],
@@ -1568,7 +1679,7 @@
       *
       * @return
       */
-    protected def user$Password$Reset(): Route = {
+    protected def $user$Passwd$Reset(): Route = {
         case class Req$User$Password$Reset(
             // Caller.
             acsTok: String,
@@ -1603,7 +1714,7 @@
       *
       * @return
       */
-    protected def user$All(): Route = {
+    protected def $user$All(): Route = {
         case class Req$User$All(
             // Caller.
             acsTok: String
@@ -1658,7 +1769,7 @@
       *
       * @return
       */
-    protected def user$Get(): Route = {
+    protected def $user$Get(): Route = {
         case class Req$User$Get(
             // Caller.
             acsTok: String,
@@ -1713,7 +1824,7 @@
       *
       * @return
       */
-    protected def probe$All(): Route = {
+    protected def $probe$All(): Route = {
         case class Req$Probe$All(
             acsTok: String
         )
@@ -1721,6 +1832,7 @@
             id: String,
             name: String,
             version: String,
+            elementIds: Set[String],
             enabledBuiltInTokens: Set[String]
         )
         case class Probe_Probe$All(
@@ -1750,7 +1862,7 @@
         )
 
         implicit val reqFmt: RootJsonFormat[Req$Probe$All] = jsonFormat1(Req$Probe$All)
-        implicit val mdlFmt: RootJsonFormat[Model_Probe$All] = jsonFormat4(Model_Probe$All)
+        implicit val mdlFmt: RootJsonFormat[Model_Probe$All] = jsonFormat5(Model_Probe$All)
         implicit val probFmt: RootJsonFormat[Probe_Probe$All] = jsonFormat19(Probe_Probe$All)
         implicit val resFmt: RootJsonFormat[Res$Probe$All] = jsonFormat2(Res$Probe$All)
 
@@ -1783,6 +1895,7 @@
                         m.id,
                         m.name,
                         m.version,
+                        m.elementIds,
                         m.enabledBuiltInTokens
                     ))
                 ))
@@ -1947,38 +2060,40 @@
                 corsHandler (
                     get {
                         withRequestTimeoutResponse(_ => timeoutResp) {
-                            path(API / "health") { health$() } // Also duplicated for POST.
+                            path(API / "health") { $health() } // Also duplicated for POST.
                         }
                     } ~
                         post {
                             encodeResponseWith(Coders.NoCoding, Coders.Gzip) {
                                 withRequestTimeoutResponse(_ => timeoutResp) {
-                                    path(API / "health") { health$() } // Duplicate for POST.
-                                    path(API / "signin") { withMetric(M_SIGNIN_LATENCY_MS, signin$) } ~
-                                    path(API / "signout") { withMetric(M_SIGNOUT_LATENCY_MS, signout$) } ~
-                                    path(API / "cancel") { withMetric(M_CANCEL_LATENCY_MS, cancel$) } ~
-                                    path(API / "check") { withMetric(M_CHECK_LATENCY_MS, check$) } ~
-                                    path(API / "clear"/ "conversation") { withMetric(M_CLEAR_CONV_LATENCY_MS, clear$Conversation) } ~
-                                    path(API / "clear"/ "dialog") { withMetric(M_CLEAR_DIALOG_LATENCY_MS, clear$Dialog) } ~
-                                    path(API / "company"/ "add") { withMetric(M_COMPANY_ADD_LATENCY_MS, company$Add) } ~
-                                    path(API / "company"/ "get") { withMetric(M_COMPANY_GET_LATENCY_MS, company$Get) } ~
-                                    path(API / "company" / "update") { withMetric(M_COMPANY_UPDATE_LATENCY_MS, company$Update) } ~
-                                    path(API / "company" / "token" / "reset") { withMetric(M_COMPANY_TOKEN_LATENCY_MS, company$Token$Reset) } ~
-                                    path(API / "company" / "delete") { withMetric(M_COMPANY_DELETE_LATENCY_MS, company$Delete) } ~
-                                    path(API / "user" / "get") { withMetric(M_USER_GET_LATENCY_MS, user$Get) } ~
-                                    path(API / "user" / "add") { withMetric(M_USER_ADD_LATENCY_MS, user$Add) } ~
-                                    path(API / "user" / "update") { withMetric(M_USER_UPDATE_LATENCY_MS, user$Update) } ~
-                                    path(API / "user" / "delete") { withMetric(M_USER_DELETE_LATENCY_MS, user$Delete) } ~
-                                    path(API / "user" / "admin") { withMetric(M_USER_ADMIN_LATENCY_MS, user$Admin) } ~
-                                    path(API / "user" / "passwd" / "reset") { withMetric(M_USER_PASSWD_RESET_LATENCY_MS, user$Password$Reset) } ~
-                                    path(API / "user" / "all") { withMetric(M_USER_ALL_LATENCY_MS, user$All) } ~
-                                    path(API / "feedback"/ "add") { withMetric(M_FEEDBACK_ADD_LATENCY_MS, feedback$Add) } ~
-                                    path(API / "feedback"/ "all") { withMetric(M_FEEDBACK_GET_LATENCY_MS, feedback$All) } ~
-                                    path(API / "feedback" / "delete") { withMetric(M_FEEDBACK_DELETE_LATENCY_MS, feedback$Delete) } ~
-                                    path(API / "probe" / "all") { withMetric(M_PROBE_ALL_LATENCY_MS, probe$All) } ~
-                                    path(API / "model" / "sugsyn") { withMetric(M_MODEL_SUGSYN_LATENCY_MS, sugsyn$) } ~
-                                    path(API / "ask") { withMetric(M_ASK_LATENCY_MS, ask$) } ~
-                                    path(API / "ask" / "sync") { withMetric(M_ASK_SYNC_LATENCY_MS, ask$Sync) }
+                                    path(API / "health") { $health() } // Duplicate for POST.
+                                    path(API / "signin") { withMetric(M_SIGNIN_LATENCY_MS, $signin) } ~
+                                    path(API / "signout") { withMetric(M_SIGNOUT_LATENCY_MS, $signout) } ~
+                                    path(API / "cancel") { withMetric(M_CANCEL_LATENCY_MS, $cancel) } ~
+                                    path(API / "check") { withMetric(M_CHECK_LATENCY_MS, $check) } ~
+                                    path(API / "clear"/ "conversation") { withMetric(M_CLEAR_CONV_LATENCY_MS, $clear$Conversation) } ~
+                                    path(API / "clear"/ "dialog") { withMetric(M_CLEAR_DIALOG_LATENCY_MS, $clear$Dialog) } ~
+                                    path(API / "company"/ "add") { withMetric(M_COMPANY_ADD_LATENCY_MS, $company$Add) } ~
+                                    path(API / "company"/ "get") { withMetric(M_COMPANY_GET_LATENCY_MS, $company$Get) } ~
+                                    path(API / "company" / "update") { withMetric(M_COMPANY_UPDATE_LATENCY_MS, $company$Update) } ~
+                                    path(API / "company" / "token" / "reset") { withMetric(M_COMPANY_TOKEN_LATENCY_MS, $company$Token$Reset) } ~
+                                    path(API / "company" / "delete") { withMetric(M_COMPANY_DELETE_LATENCY_MS, $company$Delete) } ~
+                                    path(API / "user" / "get") { withMetric(M_USER_GET_LATENCY_MS, $user$Get) } ~
+                                    path(API / "user" / "add") { withMetric(M_USER_ADD_LATENCY_MS, $user$Add) } ~
+                                    path(API / "user" / "update") { withMetric(M_USER_UPDATE_LATENCY_MS, $user$Update) } ~
+                                    path(API / "user" / "delete") { withMetric(M_USER_DELETE_LATENCY_MS, $user$Delete) } ~
+                                    path(API / "user" / "admin") { withMetric(M_USER_ADMIN_LATENCY_MS, $user$Admin) } ~
+                                    path(API / "user" / "passwd" / "reset") { withMetric(M_USER_PASSWD_RESET_LATENCY_MS, $user$Passwd$Reset) } ~
+                                    path(API / "user" / "all") { withMetric(M_USER_ALL_LATENCY_MS, $user$All) } ~
+                                    path(API / "feedback"/ "add") { withMetric(M_FEEDBACK_ADD_LATENCY_MS, $feedback$Add) } ~
+                                    path(API / "feedback"/ "all") { withMetric(M_FEEDBACK_GET_LATENCY_MS, $feedback$All) } ~
+                                    path(API / "feedback" / "delete") { withMetric(M_FEEDBACK_DELETE_LATENCY_MS, $feedback$Delete) } ~
+                                    path(API / "probe" / "all") { withMetric(M_PROBE_ALL_LATENCY_MS, $probe$All) } ~
+                                    path(API / "model" / "sugsyn") { withMetric(M_MODEL_SUGSYN_LATENCY_MS, $model$sugsyn) } ~
+                                    path(API / "model" / "syns") { withMetric(M_MODEL_SYNS_LATENCY_MS, $model$syns) } ~
+                                    path(API / "model" / "info") { withMetric(M_MODEL_SYNS_LATENCY_MS, $model$info) } ~
+                                    path(API / "ask") { withMetric(M_ASK_LATENCY_MS, $ask) } ~
+                                    path(API / "ask" / "sync") { withMetric(M_ASK_SYNC_LATENCY_MS, $ask$Sync) }
                                 }
                             }
                         }
diff --git a/nlpcraft/src/main/scala/org/apache/nlpcraft/server/sugsyn/NCSuggestSynonymManager.scala b/nlpcraft/src/main/scala/org/apache/nlpcraft/server/sugsyn/NCSuggestSynonymManager.scala
index 76af8a0..d89ba98 100644
--- a/nlpcraft/src/main/scala/org/apache/nlpcraft/server/sugsyn/NCSuggestSynonymManager.scala
+++ b/nlpcraft/src/main/scala/org/apache/nlpcraft/server/sugsyn/NCSuggestSynonymManager.scala
@@ -143,7 +143,7 @@
 
             val promise = Promise[NCSuggestSynonymResult]()
 
-            NCProbeManager.getModelInfo(mdlId, parent).onComplete {
+            NCProbeManager.getModelSynonymsInfo(mdlId, parent).onComplete {
                 case Success(m) =>
                     try {
                         require(
@@ -218,13 +218,13 @@
                                 }).
                                 toMap
 
-                            val elemSyns =
-                                mdlSyns.map { case (elemId, syns) => elemId -> syns.flatMap(parser.expand) }.
+                            val elmSyns =
+                                mdlSyns.map { case (elmId, syns) => elmId -> syns.flatMap(parser.expand) }.
                                     map { case (id, seq) => id -> seq.map(txt => split(txt).map(p => Word(p, toStemWord(p)))) }
 
                             val allReqs =
-                                elemSyns.map {
-                                    case (elemId, syns) =>
+                                elmSyns.map {
+                                    case (elmId, syns) =>
                                         // Current implementation supports suggestions only for single words synonyms.
                                         val normSyns: Seq[Seq[Word]] = syns.filter(_.size == 1)
                                         val synsStems = normSyns.map(_.map(_.stem))
@@ -248,7 +248,7 @@
                                                                 }
                                                         }.mkString(" "),
                                                         ex = exWords.mkString(" "),
-                                                        elmId = elemId,
+                                                        elmId = elmId,
                                                         index = idx
                                                     )
                                                 }
@@ -257,20 +257,20 @@
                                                     yield mkRequestData(idx, synStems, i)).distinct
                                             }
 
-                                        elemId -> reqs.toSet
+                                        elmId -> reqs.toSet
                                 }.filter(_._2.nonEmpty)
 
-                            val noExElems =
+                            val noExElms =
                                 mdlSyns.
-                                    filter { case (elemId, syns) => syns.nonEmpty && !allReqs.contains(elemId) }.
-                                    map { case (elemId, _) => elemId }
+                                    filter { case (elmId, syns) => syns.nonEmpty && !allReqs.contains(elmId) }.
+                                    map { case (elmId, _) => elmId }
 
-                            if (noExElems.nonEmpty)
+                            if (noExElms.nonEmpty)
                                 warns += s"Elements do not have *single word* synonyms in their @NCIntentSample or @NCIntentSampleRef annotations - " +
-                                    s"no suggestion can be made: ${noExElems.mkString(", ")}"
+                                    s"no suggestion can be made: ${noExElms.mkString(", ")}"
 
                             val allReqsCnt = allReqs.map(_._2.size).sum
-                            val allSynsCnt = elemSyns.map(_._2.size).sum
+                            val allSynsCnt = elmSyns.map(_._2.size).sum
 
                             logger.trace(s"Request is going to execute on 'ctxword' server [" +
                                 s"exs=${exs.size}, " +
@@ -289,7 +289,7 @@
                                 val cli = HttpClients.createDefault
                                 val err = new AtomicReference[Throwable]()
 
-                                for ((elemId, reqs) <- allReqs; batch <- reqs.sliding(BATCH_SIZE, BATCH_SIZE).map(_.toSeq)) {
+                                for ((elmId, reqs) <- allReqs; batch <- reqs.sliding(BATCH_SIZE, BATCH_SIZE).map(_.toSeq)) {
                                     U.asFuture(
                                         _ => {
                                             val post = new HttpPost(url)
@@ -322,7 +322,7 @@
                                             logger.debug(s"Executed: $i requests...")
 
                                             allSgsts.
-                                                computeIfAbsent(elemId, (_: String) => new CopyOnWriteArrayList[Suggestion]()).
+                                                computeIfAbsent(elmId, (_: String) => new CopyOnWriteArrayList[Suggestion]()).
                                                 addAll(resps.flatten.asJava)
 
                                             if (i == allReqsCnt)
@@ -342,13 +342,13 @@
                                 if (err.get() != null)
                                     throw new NCE("Error while working with 'ctxword' server.", err.get())
 
-                                val allSynsStems = elemSyns.flatMap(_._2).toSeq.flatten.map(_.stem).toSet
+                                val allSynsStems = elmSyns.flatMap(_._2).toSeq.flatten.map(_.stem).toSet
 
                                 val nonEmptySgsts = allSgsts.asScala.map(p => p._1 -> p._2.asScala).filter(_._2.nonEmpty)
 
                                 val res = mutable.HashMap.empty[String, mutable.ArrayBuffer[SuggestionResult]]
 
-                                nonEmptySgsts.foreach { case (elemId, elemSgsts) =>
+                                nonEmptySgsts.foreach { case (elmId, elemSgsts) =>
                                     elemSgsts.
                                         map(sgst => (sgst, toStem(sgst.word))).
                                         groupBy { case (_, stem) => stem }.
@@ -366,12 +366,12 @@
                                         zipWithIndex.
                                         foreach { case ((word, _, sumFactor), _) =>
                                             val seq =
-                                                res.get(elemId) match {
+                                                res.get(elmId) match {
                                                     case Some(seq) => seq
                                                     case None =>
                                                         val buf = mutable.ArrayBuffer.empty[SuggestionResult]
 
-                                                        res += elemId -> buf
+                                                        res += elmId -> buf
 
                                                         buf
                                                 }
diff --git a/nlpcraft/src/test/scala/org/apache/nlpcraft/NCTestElement.scala b/nlpcraft/src/test/scala/org/apache/nlpcraft/NCTestElement.scala
index dc465d8..3598d2e 100644
--- a/nlpcraft/src/test/scala/org/apache/nlpcraft/NCTestElement.scala
+++ b/nlpcraft/src/test/scala/org/apache/nlpcraft/NCTestElement.scala
@@ -17,7 +17,7 @@
 
 package org.apache.nlpcraft
 
-import org.apache.nlpcraft.model.NCElement
+import org.apache.nlpcraft.model.{NCElement, NCValue}
 
 import java.util
 import scala.jdk.CollectionConverters.{SeqHasAsJava, SetHasAsJava}
@@ -27,8 +27,11 @@
   * Simple test element.
   */
 case class NCTestElement(id: String, syns: String*) extends NCElement {
+    private val values = new util.ArrayList[NCValue]
+
     override def getId: String = id
     override def getSynonyms: util.List[String] = (syns :+ id).asJava
+    override def getValues: util.List[NCValue] = values
 }
 
 /**
@@ -38,4 +41,19 @@
     private def to(e: NCTestElement): NCElement = e
 
     implicit def to(set: Set[NCTestElement]): util.Set[NCElement] = set.map(to).asJava
+
+    def apply(id: String, syns: Seq[String], vals: Map[String, Seq[String]]): NCTestElement = {
+        val e = NCTestElement(id, syns :_*)
+
+        e.getValues.addAll(
+            vals.map { case (value, syns) =>
+                new NCValue {
+                    override def getName: String = value
+                    override def getSynonyms: util.List[String] = syns.asJava
+                }
+            }.toSeq.asJava
+        )
+
+        e
+    }
 }
\ No newline at end of file
diff --git a/nlpcraft/src/test/scala/org/apache/nlpcraft/server/rest/NCRestModelSpec.scala b/nlpcraft/src/test/scala/org/apache/nlpcraft/server/rest/NCRestModelSpec.scala
index d47a297..d7d22b8 100644
--- a/nlpcraft/src/test/scala/org/apache/nlpcraft/server/rest/NCRestModelSpec.scala
+++ b/nlpcraft/src/test/scala/org/apache/nlpcraft/server/rest/NCRestModelSpec.scala
@@ -17,19 +17,21 @@
 
 package org.apache.nlpcraft.server.rest
 
-import org.apache.nlpcraft.NCTestEnvironment
+import org.apache.nlpcraft.model.NCElement
+import org.apache.nlpcraft.{NCTestElement, NCTestEnvironment}
 import org.junit.jupiter.api.Assertions._
 import org.junit.jupiter.api.Test
 
-import scala.jdk.CollectionConverters.ListHasAsScala
+import java.util
+import scala.jdk.CollectionConverters.{ListHasAsScala, MapHasAsJava, SetHasAsJava, SetHasAsScala}
 
 /**
   * Note that context word server should be started.
   */
 @NCTestEnvironment(model = classOf[RestTestModel], startClient = false)
-class NCRestModelSpec extends NCRestSpec {
+class NCRestModelSpec1 extends NCRestSpec {
     @Test
-    def test(): Unit = {
+    def testSugsyn(): Unit = {
         def extract(data: JList[java.util.Map[String, Object]]): Seq[Double] =
             data.asScala.map(_.get("score").asInstanceOf[Number].doubleValue()).toSeq
 
@@ -54,5 +56,70 @@
                 assertTrue(scores.forall(s => s >= 0.5 && s <= 1))
             })
         )
+
+        postError("model/sugsyn", 400, "NC_INVALID_FIELD", "mdlId" -> "UNKNOWN")
+        postError("model/sugsyn", 400, "NC_INVALID_FIELD", "mdlId" -> "rest.test.model", "minScore" -> 2)
+        postError("model/sugsyn", 400, "NC_ERROR")
+    }
+}
+
+class RestTestModelExt extends RestTestModel {
+    override def getMacros: util.Map[String, String] = {
+        Map(
+            "<M1>" -> "mtest1 {x|_}",
+            "<M2>" -> "<M1> mtest2 {mtest3|_}"
+        ).asJava
+    }
+
+    override def getElements: util.Set[NCElement] = {
+        (
+            super.getElements.asScala ++
+            Set(
+                NCTestElement("eExt1", "<M1>", "<M1> more"),
+                NCTestElement("eExt2", Seq("<M1>", "<M1> more"), Map("v1"-> Seq("<M2>", "<M2> more"), "v2" -> Seq("<M2>")))
+            )
+        ).asJava
+    }
+}
+
+/**
+  *
+  */
+@NCTestEnvironment(model = classOf[RestTestModelExt], startClient = false)
+class NCRestModelSpec2 extends NCRestSpec {
+    @Test
+    def testSyns(): Unit = {
+        // Note that checked values are valid for current configuration of `RestTestModelExt` model.
+        def post0(elemId: String, valsShouldBe: Boolean): Unit =
+            post("model/syns", "mdlId" -> "rest.test.model", "elmId" -> elemId)(
+                ("$.status", (status: String) => assertEquals("API_OK", status)),
+                ("$.synonyms", (data: ResponseList) => assertTrue(!data.isEmpty)),
+                ("$.synonymsExp", (data: ResponseList) => assertTrue(!data.isEmpty)),
+                ("$.values", (data: java.util.Map[Object, Object]) =>
+                    if (valsShouldBe) assertTrue(!data.isEmpty) else assertTrue(data.isEmpty)),
+                ("$.valuesExp", (data: java.util.Map[Object, Object]) =>
+                    if (valsShouldBe) assertTrue(!data.isEmpty) else assertTrue(data.isEmpty)
+                )
+            )
+
+        post0("eExt1", valsShouldBe = false)
+        post0("eExt2", valsShouldBe = true)
+
+        postError("model/syns", 400, "NC_INVALID_FIELD", "mdlId" -> "UNKNOWN", "elmId" -> "UNKNOWN")
+        postError("model/syns", 400, "NC_INVALID_FIELD", "mdlId" -> "rest.test.model", "elmId" -> "UNKNOWN")
+        postError("model/syns", 400, "NC_INVALID_FIELD", "mdlId" -> ("A" * 33), "elmId" -> "UNKNOWN")
+        postError("model/syns", 400, "NC_INVALID_FIELD", "mdlId" -> "rest.test.model", "elmId" -> ("A" * 65))
+        postError("model/syns", 400, "NC_ERROR", "mdlId" -> "rest.test.model")
+    }
+
+    @Test
+    def testModelInfo(): Unit = {
+        post("model/info", "mdlId" -> "rest.test.model")(
+            ("$.status", (status: String) => assertEquals("API_OK", status)),
+            ("$.model", (data: java.util.Map[Object, Object]) => assertTrue(!data.isEmpty))
+        )
+
+        postError("model/info", 400, "NC_INVALID_FIELD", "mdlId" -> "UNKNOWN")
+        postError("model/info", 400, "NC_ERROR")
     }
 }
diff --git a/nlpcraft/src/test/scala/org/apache/nlpcraft/server/rest/NCRestSpec.scala b/nlpcraft/src/test/scala/org/apache/nlpcraft/server/rest/NCRestSpec.scala
index 03bf811..cb7d075 100644
--- a/nlpcraft/src/test/scala/org/apache/nlpcraft/server/rest/NCRestSpec.scala
+++ b/nlpcraft/src/test/scala/org/apache/nlpcraft/server/rest/NCRestSpec.scala
@@ -32,7 +32,6 @@
 import org.junit.jupiter.api.{AfterEach, BeforeEach}
 
 import java.util
-import java.util.{List => JList}
 import java.util.UUID
 
 import scala.jdk.CollectionConverters.{ListHasAsScala, MapHasAsJava, MapHasAsScala, SeqHasAsJava}
diff --git a/nlpcraft/src/test/scala/org/apache/nlpcraft/server/rest/RestTestModel.scala b/nlpcraft/src/test/scala/org/apache/nlpcraft/server/rest/RestTestModel.scala
index 8099e39..a46ee0f 100644
--- a/nlpcraft/src/test/scala/org/apache/nlpcraft/server/rest/RestTestModel.scala
+++ b/nlpcraft/src/test/scala/org/apache/nlpcraft/server/rest/RestTestModel.scala
@@ -18,7 +18,7 @@
 package org.apache.nlpcraft.server.rest
 
 import org.apache.nlpcraft.NCTestElement
-import org.apache.nlpcraft.model.{NCElement, NCIntent, NCIntentSample, NCModelAdapter, NCResult}
+import org.apache.nlpcraft.model.{NCElement, NCIntent, NCIntentSample, NCModelAdapter, NCResult, NCValue}
 
 import java.util
 
@@ -49,7 +49,8 @@
             NCTestElement("a"),
             NCTestElement("b"),
             NCTestElement("x", "cat"),
-            NCTestElement("meta")
+            NCTestElement("meta"),
+            NCTestElement("valElem", Seq("valElem1"), Map("v1"-> Seq("v11", "v12"), "v2" -> Seq("v21")))
         )
 
     @NCIntent("intent=onA term(t)={tok_id() == 'a'}")
diff --git a/openapi/nlpcraft_swagger.yml b/openapi/nlpcraft_swagger.yml
index 4b0be1e..4391cb3 100644
--- a/openapi/nlpcraft_swagger.yml
+++ b/openapi/nlpcraft_swagger.yml
@@ -234,14 +234,138 @@
           description: Failed operation.
           schema:
             $ref: '#/definitions/Error'
+  /model/syns:
+    post:
+      tags:
+        - Tools
+      summary: Gets model element expanded synonyms and values.
+      description: >-
+        Gets model element expanded synonyms and values.
+        Administrative privileges required.
+      operationId: syns
+      parameters:
+        - in: body
+          name: Payload body
+          description: JSON request.
+          required: true
+          schema:
+            type: object
+            required:
+              - acsTok
+              - mdlId
+              - elmId
+            properties:
+              acsTok:
+                type: string
+                description: Access token obtain via '/signin' call.
+                maxLength: 256
+              mdlId:
+                type: string
+                maxLength: 32
+                description: ID of the model to run get elements synonyms and values.
+              elmId:
+                type: string
+                maxLength: 64
+                description: ID of the model element to get its synonyms and values.
+      responses:
+        '200':
+          description: Successful operation.
+          schema:
+            type: object
+            required:
+              - status
+              - synonyms
+              - values
+              - synonymsExp
+              - valuesExp
+            properties:
+              status:
+                type: string
+                description: Status code of this operation.
+                enum:
+                  - API_OK
+              synonyms:
+                type: object
+                description: List of individual synomyms as they are specified in the model.
+              synonymsExp:
+                type: object
+                description: List of expanded synomyms as the model uses them (before stemmatization).
+              values:
+                type: object
+                description: >-
+                  Map of element values, if any, as they are specified in the model.
+                  Map key is the value name.
+                  Map value is a list of synonyms for that value.
+                  Empty if element doesn't have values.
+              valuesExp:
+                type: object
+                description: >-
+                  Map of element values, if any, as the model uses them (before stemmatization).
+                  Map key is the value name.
+                  Map value is a list of expanded
+                  synonyms for that value.
+                  Empty if element doesn't have values.
+        '400':
+          description: Failed operation.
+          schema:
+            $ref: '#/definitions/Error'
+  /model/info:
+    post:
+      tags:
+        - Tools
+      summary: Gets model information.
+      description: >-
+        Gets model configuration properties and elements.
+        Administrative privileges required.
+      operationId: syns
+      parameters:
+        - in: body
+          name: Payload body
+          description: JSON request.
+          required: true
+          schema:
+            type: object
+            required:
+              - acsTok
+              - mdlId
+            properties:
+              acsTok:
+                type: string
+                description: Access token obtain via '/signin' call.
+                maxLength: 256
+              mdlId:
+                type: string
+                maxLength: 32
+                description: ID of the model to get information for.
+      responses:
+        '200':
+          description: Successful operation.
+          schema:
+            type: object
+            required:
+              - status
+              - model
+            properties:
+              status:
+                type: string
+                description: Status code of this operation.
+                enum:
+                  - API_OK
+              model:
+                type: object
+                description: Object representing model configuration properties and elements.
+        '400':
+          description: Failed operation.
+          schema:
+          $ref: '#/definitions/Error'
   /check:
     post:
       tags:
         - Asking
       summary: Gets status and result of submitted requests.
       description: >-
-          Gets the status and result (OK or failure) of the previously submitted requests.
-          Request statuses returned sorted by their registration time, starting from newest.
+        Gets the status and result (OK or failure) of the previously submitted requests.
+        Request statuses returned sorted by their registration time, starting from newest.
       operationId: check
       parameters:
         - in: body