# Conflicts:
#	pom.xml
diff --git a/pom.xml b/pom.xml
index 6567003..fec112e 100644
--- a/pom.xml
+++ b/pom.xml
@@ -29,7 +29,7 @@
         <commons.validator.ver>1.7</commons.validator.ver>
         <gson.ver>2.8.6</gson.ver>
         <log4j.ver>2.14.0</log4j.ver>
-        <nlpcraft.ver>0.8.0</nlpcraft.ver>
+        <nlpcraft.ver>0.9.0</nlpcraft.ver>
         <junit.ver>5.7.0</junit.ver>
         <maven.compiler.version>3.8.0</maven.compiler.version>
         <maven.shade.plugin.ver>3.2.4</maven.shade.plugin.ver>
@@ -48,7 +48,7 @@
     <name>NLPCraft - Java Client</name>
     <groupId>org.apache.nlpcraft</groupId>
     <artifactId>java-client</artifactId>
-    <version>0.8.0</version>
+    <version>0.9.0</version>
     <url>https://nlpcraft.apache.org</url>
     <description>An open source API to convert natural language into actions.</description>
 
@@ -85,7 +85,7 @@
         <connection>scm:git:ssh://git@github.com/apache/incubator-nlpcraft.git</connection>
         <developerConnection>scm:git:ssh://git@github.com/apache/incubator-nlpcraft.git</developerConnection>
         <!-- Set actual tag name here -->
-        <tag>v0.8.0</tag>
+        <tag>v0.9.0</tag>
     </scm>
 
     <dependencies>
@@ -118,12 +118,7 @@
             <groupId>org.apache.nlpcraft</groupId>
             <artifactId>nlpcraft</artifactId>
             <version>${nlpcraft.ver}</version>
-            <exclusions>
-                <exclusion>
-                    <groupId>edu.stanford.nlp</groupId>
-                    <artifactId>*</artifactId>
-                </exclusion>
-            </exclusions>
+            <scope>test</scope>
         </dependency>
         <dependency>
             <groupId>org.junit.jupiter</groupId>
@@ -394,7 +389,7 @@
                                     ===========================================
                                 -->
                                 <!-- Reason: 'contextWordServer' should be started. -->
-                                <exclude>**/NCModelTest.*</exclude>
+                                <exclude>**/NCModelTest2.*</exclude>
                             </excludes>
                         </configuration>
                     </execution>
diff --git a/src/main/java/org/apache/nlpcraft/client/NCClient.java b/src/main/java/org/apache/nlpcraft/client/NCClient.java
index 85d8552..ad5c59d 100644
--- a/src/main/java/org/apache/nlpcraft/client/NCClient.java
+++ b/src/main/java/org/apache/nlpcraft/client/NCClient.java
@@ -32,7 +32,7 @@
  * <b>External User ID</b><br>
  * Several methods on Java client accept external "on-behalf-of" user ID (<code>usrExtId</code>) additionally to the regular
  * user ID (<code>usrID</code>). In these methods zero, one or both IDs should be provided. If none are provided
- * the ID of the currently signed in user will be used, and if both are provided they should point to the same user.
+ * the ID of the currently signed-in user will be used, and if both are provided they should point to the same user.
  * External user ID allows to use user identification from the external systems without a need to import all the
  * existing users into NLPCraft in the first place.
  * <p>
@@ -59,33 +59,25 @@
  */
 public interface NCClient {
     /**
-     * Gets current signed in user email for this client.
+     * Gets current signed-in user email for this client.
      *
-     * @return Current signed in user email for this client.
+     * @return Current signed-in user email for this client.
      * @see NCClientBuilder
      */
     String getClientUserEmail();
 
     /**
-     * Gets current signed in user password for this client.
+     * Gets current signed-in user password for this client.
      *
-     * @return Current signed in user password for this client.
+     * @return Current signed-in user password for this client.
      * @see NCClientBuilder
      */
     String getClientUserPassword();
 
     /**
-     * Gets whether or not this client is configured with embedded probe.
-     *
-     * @return Whether or not this client is configured with embedded probe.
-     * @see NCClientBuilder
-     */
-    boolean isClientEmbeddedMode();
-
-    /**
-     * Gets whether or not this client is configured with cancel-on-exit logic.
+     * Gets whether this client is configured with cancel-on-exit logic.
      * 
-     * @return Whether or not this client is configured with cancel-on-exit logic
+     * @return Whether this client is configured with cancel-on-exit logic
      * @see NCClientBuilder
      */
     boolean isClientCancelOnExit();
@@ -103,7 +95,7 @@
      * <p>
      * <b>User IDs</b><br>
      * This method allows multiple ways of specifying the ID of the user. If neither <code>usrId</code>
-     * or <code>usrExtId</code> are provided (both are <code>null</code>) then the currently signed in user ID
+     * or <code>usrExtId</code> are provided (both are <code>null</code>) then the currently signed-in user ID
      * of this client instance will be used by default. If both user IDs are provided they must identify the same
      * user in NLPCraft. If only external "on-behalf-of" <code>usrExtId</code> parameter is provided and such user
      * doesn't yet exist in NLPCraft - it will be automatically created. Note that only admin users can specify
@@ -123,7 +115,7 @@
      * <p>
      * <b>User IDs</b><br>
      * This method allows multiple ways of specifying the ID of the user. If neither <code>usrId</code>
-     * or <code>usrExtId</code> are provided (both are <code>null</code>) then the currently signed in user ID
+     * or <code>usrExtId</code> are provided (both are <code>null</code>) then the currently signed-in user ID
      * of this client instance will be used by default. If both user IDs are provided they must identify the same
      * user in NLPCraft. If only external "on-behalf-of" <code>usrExtId</code> parameter is provided and such user
      * doesn't yet exist in NLPCraft - it will be automatically created. Note that only admin users can specify
@@ -143,7 +135,7 @@
      * <p>
      * <b>User IDs</b><br>
      * This method allows multiple ways of specifying the ID of the user. If neither <code>usrId</code>
-     * or <code>usrExtId</code> are provided (both are <code>null</code>) then the currently signed in user ID
+     * or <code>usrExtId</code> are provided (both are <code>null</code>) then the currently signed-in user ID
      * of this client instance will be used by default. If both user IDs are provided they must identify the same
      * user in NLPCraft. If only external "on-behalf-of" <code>usrExtId</code> parameter is provided and such user
      * doesn't yet exist in NLPCraft - it will be automatically created. Note that only admin users can specify
@@ -177,7 +169,7 @@
      * <p>
      * <b>User IDs</b><br>
      * This method allows multiple ways of specifying the ID of the user. If neither <code>usrId</code>
-     * or <code>usrExtId</code> are provided (both are <code>null</code>) then the currently signed in user ID
+     * or <code>usrExtId</code> are provided (both are <code>null</code>) then the currently signed-in user ID
      * of this client instance will be used by default. If both user IDs are provided they must identify the same
      * user in NLPCraft. If only external "on-behalf-of" <code>usrExtId</code> parameter is provided and such user
      * doesn't yet exist in NLPCraft - it will be automatically created. Note that only admin users can specify
@@ -193,7 +185,7 @@
     List<NCFeedback> getAllFeedback(String srvReqId, Long usrId, String usrExtId) throws NCClientException, IOException;
 
     /**
-     * Adds new user to the company of the currently signed in user. Current signed in user must have
+     * Adds new user to the company of the currently signed-in user. Current signed-in user must have
      * administrative privileges.
      * <p>
      * <b>User IDs</b><br>
@@ -206,7 +198,7 @@
      * @param firstName New user first name.
      * @param lastName New user last name.
      * @param avatarUrl Optional new user avatar URL. Can be {@code null}.
-     * @param isAdmin Whether or not the new user will have administrative privileges.
+     * @param isAdmin Whether the new user will have administrative privileges.
      * @param properties Map of additional user-defined user properties.
      * @param extId Optional external "on-behalf-of" user ID. Can be {@code null}.
      * @return ID of the newly created user.
@@ -220,12 +212,12 @@
         String lastName,
         String avatarUrl,
         boolean isAdmin,
-        Map<String, String> properties,
+        Map<String, Object> properties,
         String extId
     ) throws NCClientException, IOException;
 
     /**
-     * Updates given user. Current signed in user must have administrative privileges.
+     * Updates given user. Current signed-in user must have administrative privileges.
      *
      * @param id User ID.
      * @param firstName Mandatory user first name.
@@ -240,12 +232,12 @@
         String firstName,
         String lastName,
         String avatarUrl,
-        Map<String, String> properties
+        Map<String, Object> properties
     ) throws NCClientException, IOException;
 
     /**
      * Resets password for the given user. Note that NLPCraft doesn't store clear text passwords and therefore
-     * passwords cannot be retrieved - they can only be reset. Current signed in user must have
+     * passwords cannot be retrieved - they can only be reset. Current signed-in user must have
      * administrative privileges.
      *
      * @param id ID of the user for which to reset the password.
@@ -256,7 +248,7 @@
     void resetUserPassword(Long id, String newPasswd) throws NCClientException, IOException;
 
     /**
-     * Grants or denies given user administrative privileges. Current signed in user must have
+     * Grants or denies given user administrative privileges. Current signed-in user must have
      * administrative privileges.
      * 
      * @param id ID of the user for which to change administrative privileges.
@@ -268,11 +260,11 @@
 
     /**
      * Deletes given user. Note that you cannot delete the last admin in the company.
-     * Current signed in user must have administrative privileges.
+     * Current signed-in user must have administrative privileges.
      * <p>
      * <b>User IDs</b><br>
      * This method allows multiple ways of specifying the ID of the user. If neither <code>usrId</code>
-     * or <code>usrExtId</code> are provided (both are <code>null</code>) then the currently signed in user ID
+     * or <code>usrExtId</code> are provided (both are <code>null</code>) then the currently signed-in user ID
      * of this client instance will be used by default. If both user IDs are provided they must identify the same
      * user in NLPCraft. If only external "on-behalf-of" <code>usrExtId</code> parameter is provided and such user
      * doesn't yet exist in NLPCraft - it will be automatically created. Note that only admin
@@ -291,7 +283,7 @@
      * <p>
      * <b>User IDs</b><br>
      * This method allows multiple ways of specifying the ID of the user. If neither <code>usrId</code>
-     * or <code>usrExtId</code> are provided (both are <code>null</code>) then the currently signed in user ID
+     * or <code>usrExtId</code> are provided (both are <code>null</code>) then the currently signed-in user ID
      * of this client instance will be used by default. If both user IDs are provided they must identify the same
      * user in NLPCraft. If only external "on-behalf-of" <code>usrExtId</code> parameter is provided and such user
      * doesn't yet exist in NLPCraft - it will be automatically created. Note that only admin
@@ -306,7 +298,7 @@
     NCUser getUser(Long usrId, String usrExtId) throws NCClientException, IOException;
 
     /**
-     * Gets all user records for the current signed in user company. Current signed in user must
+     * Gets all user records for the current signed-in user company. Current signed-in user must
      * have administrative privileges.
      *
      * @return List of user records.
@@ -316,8 +308,8 @@
     List<NCUser> getAllUsers() throws NCClientException, IOException;
 
     /**
-     * Gets all active (connected to the REST server) probes for the current signed in user company.
-     * Current signed in user must have administrative privileges.
+     * Gets all active (connected to the REST server) probes for the current signed-in user company.
+     * Current signed-in user must have administrative privileges.
      * 
      * @return List of active probes.
      * @throws NCClientException Thrown in case of client-specific errors.
@@ -331,7 +323,7 @@
      * <p>
      * <b>User IDs</b><br>
      * This method allows multiple ways of specifying the ID of the user. If neither <code>usrId</code>
-     * or <code>usrExtId</code> are provided (both are <code>null</code>) then the currently signed in user ID
+     * or <code>usrExtId</code> are provided (both are <code>null</code>) then the currently signed-in user ID
      * of this client instance will be used by default. If both user IDs are provided they must identify the same
      * user in NLPCraft. If only external "on-behalf-of" <code>usrExtId</code> parameter is provided and such user
      * doesn't yet exist in NLPCraft - it will be automatically created. Note that only admin
@@ -340,7 +332,7 @@
      * @param mdlId ID of the model to submit the request to.
      * @param txt Text to process.
      * @param data Optional JSON data payload. Can be {@code null} or empty.
-     * @param enableLog Whether or not to enable processing log collection.
+     * @param enableLog Whether to enable processing log collection.
      * @param usrId Optional user ID.
      * @param usrExtId Optional external "on-behalf-of" user ID.
      * @return Server request ID of the submitted request.
@@ -374,7 +366,7 @@
      * <p>
      * <b>User IDs</b><br>
      * This method allows multiple ways of specifying the ID of the user. If neither <code>usrId</code>
-     * or <code>usrExtId</code> are provided (both are <code>null</code>) then the currently signed in user ID
+     * or <code>usrExtId</code> are provided (both are <code>null</code>) then the currently signed-in user ID
      * of this client instance will be used by default. If both user IDs are provided they must identify the same
      * user in NLPCraft. If only external "on-behalf-of" <code>usrExtId</code> parameter is provided and such user
      * doesn't yet exist in NLPCraft - it will be automatically created. Note that only admin
@@ -383,7 +375,7 @@
      * @param mdlId ID of the model to submit the request to.
      * @param txt Text to process.
      * @param data Optional JSON data payload.
-     * @param enableLog Whether or not to enable processing log collection.
+     * @param enableLog Whether to enable processing log collection.
      * @param usrId Optional user ID.
      * @param usrExtId Optional external "on-behalf-of" user ID.
      * @return Query processing result.
@@ -406,7 +398,7 @@
      * <p>
      * <b>User IDs</b><br>
      * This method allows multiple ways of specifying the ID of the user. If neither <code>usrId</code>
-     * or <code>usrExtId</code> are provided (both are <code>null</code>) then the currently signed in user ID
+     * or <code>usrExtId</code> are provided (both are <code>null</code>) then the currently signed-in user ID
      * of this client instance will be used by default. If both user IDs are provided they must identify the same
      * user in NLPCraft. If only external "on-behalf-of" <code>usrExtId</code> parameter is provided and such user
      * doesn't yet exist in NLPCraft - it will be automatically created. Note that only admin
@@ -435,7 +427,7 @@
      * <p>
      * <b>User IDs</b><br>
      * This method allows multiple ways of specifying the ID of the user. If neither <code>usrId</code>
-     * or <code>usrExtId</code> are provided (both are <code>null</code>) then the currently signed in user ID
+     * or <code>usrExtId</code> are provided (both are <code>null</code>) then the currently signed-in user ID
      * of this client instance will be used by default. If both user IDs are provided they must identify the same
      * user in NLPCraft. If only external "on-behalf-of" <code>usrExtId</code> parameter is provided and such user
      * doesn't yet exist in NLPCraft - it will be automatically created. Note that only admin
@@ -485,11 +477,11 @@
         String adminFirstName,
         String adminLastName,
         String adminAvatarUrl,
-        Map<String, String> properties
+        Map<String, Object> properties
     ) throws NCClientException, IOException;
 
     /**
-     * Gets the company descriptor for the current signed in user.
+     * Gets the company descriptor for the current signed-in user.
      *
      * @return Company descriptor.
      * @throws NCClientException Thrown in case of client-specific errors.
@@ -498,7 +490,7 @@
     NCCompany getCompany() throws NCClientException, IOException;
 
     /**
-     * Updates company information for the current signed in user. Current signed in user must have
+     * Updates company information for the current signed-in user. Current signed-in user must have
      * administrative privileges. Note that users cannot update or get information about other
      * companies.
      *
@@ -521,7 +513,7 @@
         String city,
         String address,
         String postalCode,
-        Map<String, String> properties
+        Map<String, Object> properties
     ) throws NCClientException, IOException;
 
     /**
@@ -555,7 +547,13 @@
      * @throws IOException Thrown in case of generic I/O errors.
      * @see <a href="https://nlpcraft.apache.org/tools/syn_tool.html">Synonym suggestion tool</a>.
      */
-    NCSuggestionData suggestSynonyms(String mdlId, Double minScore)  throws NCClientException, IOException;
+    NCSuggestionData suggestSynonyms(String mdlId, Double minScore) throws NCClientException, IOException;
+
+    // TODO:
+    NCElementSynonymsData getSynonyms(String mdlId, String elmId) throws NCClientException, IOException;
+
+    // TODO:
+    NCModelInfo getModelInfo(String mdlId) throws NCClientException, IOException;
     
     /**
      * Closes the client and signs out from the REST server. Any further calls to this client will result in
diff --git a/src/main/java/org/apache/nlpcraft/client/NCClientBuilder.java b/src/main/java/org/apache/nlpcraft/client/NCClientBuilder.java
index c0a238b..a07f559 100644
--- a/src/main/java/org/apache/nlpcraft/client/NCClientBuilder.java
+++ b/src/main/java/org/apache/nlpcraft/client/NCClientBuilder.java
@@ -38,7 +38,7 @@
  *         .build();
  *
  *     // Perform any necessary calls...
- *     NCResult res = cli.askSync("my.model.id", txt);
+ *     NCResult res = cli.askSync("my.model.id", "My question or query");
  *
  *     // Close client &amp; sign out at the end.
  *     cli.close();
@@ -53,9 +53,7 @@
     public static final String DFLT_PWD = "admin";
     /** Default cancel on exit flag. */
     public static final boolean DFLT_CANCEL_ON_EXIT = true;
-    /** Default embedded probe mode. */
-    public static final boolean DFLT_EMBEDDED_PROBE = false;
-    
+
     private final NCClientImpl impl = new NCClientImpl();
     
     /**
@@ -119,21 +117,6 @@
     }
     
     /**
-     * Sets embedded probe mode flag. If set to {@code true} the implementation
-     * will expect that the embedded probe is running in the local JVM and will receive
-     * query result via local callbacks. See <code>NCEmbeddedProbe</code> class for more details.
-     *
-     * @param embeddedMode Embedded probe mode.
-     * @return Current client builder.
-     * @see #DFLT_EMBEDDED_PROBE
-     */
-    public NCClientBuilder setEmbeddedProbe(boolean embeddedMode) {
-        impl.setEmbeddedProbe(embeddedMode);
-
-        return this;
-    }
-    
-    /**
      * Builds a client instance with configured settings.
      *
      * @return Newly created instance of {@link NCClient}.
@@ -144,7 +127,7 @@
         if (impl.getEmail() == null && impl.getPassword() != null ||
             impl.getEmail() != null && impl.getPassword() == null
         )
-            throw new IllegalArgumentException("Both email and password should be null or not null");
+            throw new IllegalArgumentException("Both email and password should be null or not null.");
         
         if (impl.getBaseUrl() == null)
             impl.setBaseUrl(DFLT_BASEURL);
@@ -161,9 +144,6 @@
         if (impl.isCancelOnExit() == null)
             impl.setCancelOnExit(DFLT_CANCEL_ON_EXIT);
 
-        if (impl.isEmbeddedProbe() == null)
-            impl.setEmbeddedProbe(DFLT_EMBEDDED_PROBE);
-
         impl.initialize();
         
         return impl;
diff --git a/src/main/java/org/apache/nlpcraft/client/NCCompany.java b/src/main/java/org/apache/nlpcraft/client/NCCompany.java
index 93baac5..68be2d0 100644
--- a/src/main/java/org/apache/nlpcraft/client/NCCompany.java
+++ b/src/main/java/org/apache/nlpcraft/client/NCCompany.java
@@ -86,5 +86,5 @@
      *
      * @return Optional company properties (metadata).
      */
-    Map<String, String> getProperties();
+    Map<String, Object> getProperties();
 }
diff --git a/src/main/java/org/apache/nlpcraft/client/NCElement.java b/src/main/java/org/apache/nlpcraft/client/NCElement.java
new file mode 100644
index 0000000..d06c086
--- /dev/null
+++ b/src/main/java/org/apache/nlpcraft/client/NCElement.java
@@ -0,0 +1,150 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.nlpcraft.client;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+/**
+ * A model's element descriptor.
+ *
+ * @see NCModelInfo#getElements() 
+ */
+public interface NCElement {
+    /**
+     * Gets unique ID of this element.
+     * <p>
+     * This unique ID should be human-readable for simpler debugging and testing of the model.
+     * Although element ID could be any arbitrary string it is highly recommended having
+     * element ID as a lower case string starting with some model prefix, followed by colon and
+     * then the element's name. For example, some built-in NLPCraft IDs are: <code>nlpcraft:date</code>,
+     * <code>nlpcraft:city</code>.
+     *
+     * @return Unique ID of this element.
+     */
+    String getId();
+
+    /**
+     * Gets the list of groups this element belongs to.
+     * <p>
+     * Model element can belong to one or more groups. By default, the element belongs to a single group whose group
+     * ID is equal to its {@link #getId() ID}.
+     *
+     * @return List of groups this element belongs to. By default, the model element belongs to one group
+     *      with ID equal to the element {@link #getId() ID}.
+     * @see #getId()
+     */
+    List<String> getGroups();
+
+    /**
+     * Gets optional user-defined element's metadata.
+     *
+     * @return Element's metadata or empty collection if none provided. implementation return empty collection.
+     */
+    Map<String, Object> getMetadata();
+
+    /**
+     * Gets optional element description.
+     *
+     * @return Optional element description. implementation returns {@code null}.
+     */
+    String getDescription();
+
+    /**
+     * Gets optional map of {@link NCValue values} for this element.
+     * <p>
+     * Each element can generally be recognized either by one of its synonyms or values. Elements and their values
+     * are analogous to types and instances of that type in programming languages. Each value
+     * has a name and optional set of its own synonyms by which that value, and ultimately its element, can be
+     * recognized by. Note that value name itself acts as an implicit synonym even when no additional synonyms added
+     * for that value.
+     * <p>
+     * Consider this example. A model element {@code x:car} can have:
+     * <ul>
+     *      <li>
+     *          Set of general synonyms:
+     *          <code>{transportation|transport|_} {vehicle|car|sedan|auto|automobile|suv|crossover|coupe|truck}</code>
+     *      </li>
+     *      <li>Set of values:
+     *          <ul>
+     *              <li>{@code mercedes} with synonyms {@code (mercedes, mercedes-benz, mb, benz)}</li>
+     *              <li>{@code bmw} with synonyms {@code (bmw, bimmer)}</li>
+     *              <li>{@code chevrolet} with synonyms {@code (chevy, chevrolet)}</li>
+     *          </ul>
+     *      </li>
+     * </ul>
+     * With that setup {@code x:car} element will be recognized by any of the following input sub-string:
+     * <ul>
+     *      <li>{@code transport car}</li>
+     *      <li>{@code benz}</li>
+     *      <li>{@code automobile}</li>
+     *      <li>{@code transport vehicle}</li>
+     *      <li>{@code sedan}</li>
+     *      <li>{@code chevy}</li>
+     *      <li>{@code bimmer}</li>
+     *      <li>{@code x:car}</li>
+     * </ul>
+     *
+     * @return Map of value's name and its synonyms or {@code null} if not defined.
+     */
+    List<NCValue> getValues();
+
+    /**
+     * Gets optional ID of the immediate parent element. Parent ID allows model elements to form into hierarchy.
+     *
+     * @return Optional parent element ID, or {@code null} if not specified. implementation returns
+     *      {@code null}.
+     */
+    String getParentId();
+
+    /**
+     * Gets the list of synonyms by which this model element will be recognized by. Read more about
+     * many forms of synonyms in <a target=_ href="https://nlpcraft.apache.org/data-model.html">Data Model</a> section
+     * and review <a target=_ href="https://github.com/apache/incubator-nlpcraft/tree/master/nlpcraft-examples">examples</a>.
+     *
+     * @return List of synonyms for this element. List is generally optional since element's ID acts
+     *      as an implicit synonym. implementation returns an empty list.
+     */
+    List<String> getSynonyms();
+
+    /**
+     * Whether to permutate multi-word synonyms. Automatic multi-word synonyms permutations greatly
+     * increase the total number of synonyms in the system and allows for better multi-word synonym detection.
+     * For example, if permutation is allowed the synonym "a b c" will be automatically converted into a
+     * sequence of synonyms of "a b c", "b a c", "a c b".
+     * <p>
+     * This property overrides the value from {@link NCModelInfo#isPermutateSynonyms()}.
+     * One should use this property if model's value isn't applicable to this element.
+     *
+     * @return Optional synonym permutate property overriding model's one.
+     * @see NCModelInfo#isPermutateSynonyms()
+     */
+    Optional<Boolean> isPermutateSynonyms();
+
+    /**
+     * Whether this element allows the non-stop words gaps in its multi-word synonyms.
+     * <p>
+     * This property overrides the value from {@link NCModelInfo#isSparse()}.
+     * One should use this property if model's value isn't applicable to this element.
+     *
+     * @return Optional multi-word synonym sparsity property overriding model's one.
+     * @see NCModelInfo#isSparse()
+     */
+    Optional<Boolean> isSparse();
+}
diff --git a/src/main/java/org/apache/nlpcraft/client/NCElementSynonymsData.java b/src/main/java/org/apache/nlpcraft/client/NCElementSynonymsData.java
new file mode 100644
index 0000000..5af5357
--- /dev/null
+++ b/src/main/java/org/apache/nlpcraft/client/NCElementSynonymsData.java
@@ -0,0 +1,86 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.nlpcraft.client;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * A model element synonyms data descriptor.
+ *
+ * @see NCClient#getSynonyms(String, String)
+ */
+public interface NCElementSynonymsData {
+    /**
+     * Gets number of synonyms (in their unexpanded forms) for the element.
+     *
+     * @return Number of synonyms (in their unexpanded forms) for the element.
+     * @see #getSynonymsExpCnt()
+     */
+    int getSynonymsCnt();
+
+    /**
+     * Gets number of expanded synonyms for the element.
+     *
+     * @return Number of expanded synonyms for the element.
+     * @see #getSynonymsCnt()
+     */
+    int getSynonymsExpCnt();
+
+    /**
+     * Gets the rate of number of expanded synonyms over the non-expanded ones.
+     * The return value is always greater than 1.
+     *
+     * @return Rate of number of expanded synonyms over the non-expanded ones.
+     * @see #getSynonymsCnt()
+     * @see #getSynonymsExpCnt()
+     */
+    long getSynonymsExpRatePct();
+
+    /**
+     * Gets the list of non-expanded synonyms for this element.
+     *
+     * @return List of non-expanded synonyms for this element.
+     * @see #getSynonymsExp()
+     */
+    List<String> getSynonyms();
+
+    /**
+     * Gets the list of expanded synonyms for this element.
+     *
+     * @return List of expanded synonyms for this element.
+     * @see #getSynonyms()
+     */
+    List<String> getSynonymsExp();
+
+    /**
+     * Get the map of values and their non-expanded synonyms.
+     *
+     * @return Map of values and their non-expanded synonyms.
+     * @see #getValuesExp()
+     */
+    Map<String, List<String>> getValues();
+
+    /**
+     * Get the map of values and their expanded synonyms.
+     *
+     * @return Map of values and their expanded synonyms.
+     * @see #getValues()
+     */
+    Map<String, List<String>> getValuesExp();
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/nlpcraft/client/NCModel.java b/src/main/java/org/apache/nlpcraft/client/NCModel.java
index 4712df8..4e9c4ae 100644
--- a/src/main/java/org/apache/nlpcraft/client/NCModel.java
+++ b/src/main/java/org/apache/nlpcraft/client/NCModel.java
@@ -21,30 +21,31 @@
  * Data model descriptor.
  *
  * @see NCClient#getProbes()
- * @see NCProbe#getModels() 
+ * @see NCProbe#getModels()
+ * @see NCModelInfo
  */
 public interface NCModel {
     /**
      * Gets unique, <i>immutable</i> ID of this model.
      * <p>
      * Note that <b>model IDs are immutable</b> while name and version
-     * can be changed freely. Changing model ID is equal to creating a completely new model that will have
-     * to be re-trained and re-learned again. Model IDs (unlike name and version) are not exposed to
-     * the end user and only serve a technical purpose.
+     * can be changed freely. Changing model ID is equal to creating a completely new model.
+     * Model IDs (unlike name and version) are not exposed to the end user and only serve a
+     * technical purpose. ID's max length is 32 characters.
      *
      * @return Unique, <i>immutable</i> ID of this model.
      */
     String getId();
 
     /**
-     * Gets descriptive name of this model.
+     * Gets descriptive name of this model. Name's max length is 64 characters.
      *
      * @return Descriptive name for this model.
      */
     String getName();
 
     /**
-     * Gets the version of this model using semantic versioning. 
+     * Gets the version of this model using semantic versioning. Version's max length is 16 characters.
      *
      * @return A version compatible with (<a href="http://www.semver.org">www.semver.org</a>) specification.
      */
diff --git a/src/main/java/org/apache/nlpcraft/client/NCModelInfo.java b/src/main/java/org/apache/nlpcraft/client/NCModelInfo.java
new file mode 100644
index 0000000..4b86711
--- /dev/null
+++ b/src/main/java/org/apache/nlpcraft/client/NCModelInfo.java
@@ -0,0 +1,419 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.nlpcraft.client;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * A model view descriptor.
+ *
+ * @see NCClient#getModelInfo(String)
+ * @see NCModel
+ */
+public interface NCModelInfo {
+    /**
+     * Gets unique, <i>immutable</i> ID of this model.
+     * <p>
+     * Note that <b>model IDs are immutable</b> while name and version
+     * can be changed freely. Changing model ID is equal to creating a completely new model.
+     * Model IDs (unlike name and version) are not exposed to the end user and only serve a
+     * technical purpose. ID's max length is 32 characters.
+     *
+     * @return Unique, <i>immutable</i> ID of this model.
+     */
+    String getId();
+
+    /**
+     * Gets descriptive name of this model. Name's max length is 64 characters.
+     *
+     * @return Descriptive name for this model.
+     */
+    String getName();
+
+    /**
+     * Gets the version of this model using semantic versioning. Version's max length is 16 characters.
+     *
+     * @return A version compatible with (<a href="http://www.semver.org">www.semver.org</a>) specification.
+     */
+    String getVersion();
+
+    /**
+     * Gets optional short model description. This can be displayed by the management tools.
+     *
+     * @return Optional short model description.
+     */
+    String getDescription();
+
+    /**
+     * Gets the origin of this model like name of the class, file path or URL.
+     *
+     * @return Origin of this model like name of the class, file path or URL.
+     */
+    String getOrigin();
+
+    /**
+     * Gets maximum number of unknown words until automatic rejection. An unknown word is a word
+     * that is not part of Princeton WordNet database. If you expect a very formalized and well-defined
+     * input without uncommon slang and abbreviations you can set this to a small number
+     * like one or two. However, in most cases we recommend leaving it as or set it to a larger
+     * number like five or more.
+     *
+     * @return Maximum number of unknown words until automatic rejection.
+     */
+    int getMaxUnknownWords();
+
+    /**
+     * Gets maximum number of free words until automatic rejection. A free word is a known word that is
+     * not part of any recognized token. In other words, a word that is present in the user input
+     * but won't be used to understand its meaning. Setting it to a non-zero risks the misunderstanding
+     * of the user input, while setting it to zero often makes understanding logic too rigid. In most
+     * cases we recommend setting to between one and three. If you expect the user input to contain
+     * many <i>noisy</i> idioms, slang or colloquials - you can set it to a larger number.
+     *
+     * @return Maximum number of free words until automatic rejection.
+     */
+    int getMaxFreeWords();
+
+    /**
+     * Gets maximum number of suspicious words until automatic rejection. A suspicious word is a word
+     * that is defined by the model that should not appear in a valid user input under no circumstances.
+     * A typical example of suspicious words would be words "sex" or "porn" when processing
+     * queries about children books. In most cases this should be set to zero (default) to automatically
+     * reject any such suspicious words in the user input.
+     *
+     * @return Maximum number of suspicious words until automatic rejection.
+     */
+    int getMaxSuspiciousWords();
+
+    /**
+     * Gets minimum word count (<i>including</i> stopwords) below which user input will be automatically
+     * rejected as too short. In almost all cases this value should be greater than or equal to one.
+     *
+     * @return Minimum word count (<i>including</i> stopwords) below which user input will be automatically
+     * rejected as too short.
+     */
+    int getMinWords();
+
+    /**
+     * Gets maximum word count (<i>including</i> stopwords) above which user input will be automatically
+     * rejected as too long. In almost all cases this value should be greater than or equal to one.
+     *
+     * @return Maximum word count (<i>including</i> stopwords) above which user input will be automatically
+     * rejected as too long.
+     */
+    int getMaxWords();
+
+    /**
+     * Gets minimum number of all tokens (system and user defined) below which user input will be
+     * automatically rejected as too short. In almost all cases this value should be greater than or equal to one.
+     *
+     * @return Minimum number of all tokens.
+     */
+    int getMinTokens();
+
+    /**
+     * Gets maximum number of all tokens (system and user defined) above which user input will be
+     * automatically rejected as too long. Note that sentences with large number of token can result
+     * in significant processing delay and substantial memory consumption.
+     *
+     * @return Maximum number of all tokens.
+     */
+    int getMaxTokens();
+
+    /**
+     * Gets minimum word count (<i>excluding</i> stopwords) below which user input will be automatically rejected
+     * as ambiguous sentence.
+     *
+     * @return Minimum word count (<i>excluding</i> stopwords) below which user input will be automatically
+     * rejected as too short.
+     */
+    int getMinNonStopwords();
+
+    /**
+     * Whether to allow non-English language in user input.
+     * Currently, only English language is supported. However, model can choose whether
+     * to automatically reject user input that is detected to be a non-English. Note that current
+     * algorithm only works reliably on longer user input (10+ words). On short sentences it will
+     * often produce an incorrect result.
+     *
+     * @return Whether to allow non-English language in user input.
+     */
+    boolean isNonEnglishAllowed();
+
+    /**
+     * Whether to allow non-Latin charset in user input. Currently, only
+     * Latin charset is supported. However, model can choose whether to automatically reject user
+     * input with characters outside of Latin charset. If {@code false} such user input will be automatically
+     * rejected.
+     *
+     * @return Whether to allow non-Latin charset in user input.
+     */
+    boolean isNotLatinCharsetAllowed();
+
+    /**
+     * Whether to allow known English swear words in user input. If {@code false} - user input with
+     * detected known English swear words will be automatically rejected.
+     *
+     * @return Whether to allow known swear words in user input.
+     */
+    boolean isSwearWordsAllowed();
+
+    /**
+     * Whether to allow user input without a single noun. If {@code false} such user input
+     * will be automatically rejected. Typically, for strict command or query-oriented models this should be set to
+     * {@code false} as any command or query should have at least one noun subject. However, for conversational
+     * models this can be set to {@code false} to allow for a smalltalk and one-liners.
+     *
+     * @return Whether to allow user input without a single noun.
+     */
+    boolean isNoNounsAllowed();
+
+    /**
+     * Whether to permutate multi-word synonyms. Automatic multi-word synonyms permutations greatly
+     * increase the total number of synonyms in the system and allows for better multi-word synonym detection.
+     * For example, if permutation is allowed the synonym "a b c" will be automatically converted into a
+     * sequence of synonyms of "a b c", "b a c", "a c b". This property is closely related to {@link #isSparse()}
+     * which are typically changed together. Note that individual model elements can override this property using
+     * {@link NCElement#isPermutateSynonyms()} method.
+     *
+     * @return Whether to permutate multi-word synonyms.
+     * @see NCElement#isPermutateSynonyms()
+     * @see NCElement#isSparse()
+     * @see #isSparse()
+     */
+    boolean isPermutateSynonyms();
+
+    /**
+     * Whether duplicate synonyms are allowed. If {@code true} - the model will pick the random
+     * model element when multiple elements found due to duplicate synonyms. If {@code false} - model
+     * will print error message and will not deploy.
+     *
+     * @return Whether to allow duplicate synonyms.
+     */
+    boolean isDupSynonymsAllowed();
+
+    /**
+     * Total number of synonyms allowed per model. Model won't deploy if total number of synonyms exceeds this
+     * number.
+     *
+     * @return Total number of synonyms allowed per model.
+     * @see #getMaxElementSynonyms()
+     */
+    int getMaxTotalSynonyms();
+
+    /**
+     * Whether to allow the user input with no user token detected. If {@code false} such user
+     * input will be automatically rejected. Note that this property only applies to user-defined
+     * token (i.e. model element). Even if there are no user defined tokens, the user input may still
+     * contain system token like <code>nlpcraft:city</code> or <code>nlpcraft:date</code>. In many cases models
+     * should be build to allow user input without user tokens. However, set it to {@code false} if presence
+     * of at least one user token is mandatory.
+     *
+     * @return Whether to allow the user input with no user token detected.
+     */
+    boolean isNoUserTokensAllowed();
+
+    /**
+     * Whether this model elements allow non-stop words gaps in their multi-word synonyms.
+     * This property is closely related to {@link #isPermutateSynonyms()} which are typically changed together.
+     * Note that individual model elements can override this property using {@link NCElement#isSparse()}
+     * method.
+     *
+     * @return Optional multi-word synonym sparsity model property.
+     * @see NCElement#isSparse()
+     * @see NCElement#isPermutateSynonyms()
+     * @see #isPermutateSynonyms()
+     */
+    boolean isSparse();
+
+    /**
+     * Gets optional user defined model metadata that can be set by the developer and accessed later.
+     * By default, it returns an empty map. Note that this metadata is mutable and can be
+     * changed at runtime by the model's code.
+     *
+     * @return Optional user defined model metadata. By default, returns an empty map. Never returns {@code null}.
+     */
+    Map<String, Object> getMetadata();
+
+    /**
+     * Gets an optional list of stopwords to add to the built-in ones.
+     * <p>
+     * Stopword is an individual word (i.e. sequence of characters excluding whitespaces) that contribute no
+     * semantic meaning to the sentence. For example, 'the', 'wow', or 'hm' provide no semantic meaning to the
+     * sentence and can be safely excluded from semantic analysis.
+     * <p>
+     * NLPCraft comes with a carefully selected list of English stopwords which should be sufficient
+     * for a majority of use cases. However, you can add additional stopwords to this list. The typical
+     * use for user-defined stopwords are jargon parasite words that are specific to the model's domain.
+     *
+     * @return Potentially empty list of additional stopwords.
+     */
+    Set<String> getAdditionalStopWords();
+
+    /**
+     * Gets an optional list of stopwords to exclude from the built-in list of stopwords.
+     * <p>
+     * Just like you can add additional stopwords via {@link #getAdditionalStopWords()} you can exclude
+     * certain words from the list of stopwords. This can be useful in rare cases when built-in
+     * stopword has specific meaning of your model. In order to process them you need to exclude them
+     * from the list of stopwords.
+     *
+     * @return Potentially empty list of excluded stopwords.
+     */
+    Set<String> getExcludedStopWords();
+
+    /**
+     * Gets an optional list of suspicious words. A suspicious word is a word that generally should not appear in user
+     * sentence when used with this model. For example, if a particular model is for children oriented book search,
+     * the words "sex" and "porn" should probably NOT appear in the user input and can be automatically rejected
+     * when added here and model's metadata {@code MAX_SUSPICIOUS_WORDS} property set to zero.
+     *
+     * @return Potentially empty list of suspicious words in their lemma form.
+     */
+    Set<String> getSuspiciousWords();
+
+    /**
+     * Gets an optional map of macros to be used in this model. Macros and option groups are instrumental
+     * in defining model's elements.
+     *
+     * @return Potentially empty map of macros.
+     */
+    Map<String, String> getMacros();
+
+    /**
+     * Gets a set of model elements or named entities. Model can have zero or more user defined elements.
+     *
+     * @return Set of model elements, potentially empty.
+     */
+    Set<NCElement> getElements();
+
+    /**
+     * Gets a set of IDs for built-in named entities (tokens) that should be enabled and detected for this model.
+     * Unless model requests (i.e. enables) the built-in tokens in this method the NLP subsystem will not attempt
+     * to detect them. Explicit enablement of the token significantly improves the overall performance by avoiding
+     * unnecessary token detection. Note that you don't have to specify your own user elements here as they are
+     * always enabled.
+     *
+     * @return Set of built-in tokens, potentially empty but never {@code null}, that should be enabled
+     *      and detected for this model.
+     */
+    Set<String> getEnabledBuiltInTokens();
+
+    /**
+     * Gets s set of named entities (token) IDs that will be considered as abstract tokens.
+     * An abstract token is only detected when it is either a constituent part of some other non-abstract token
+     * or referenced by built-in tokens. In other words, an abstract token will not be detected in a standalone
+     * unreferenced position. By (unless returned by this method), all named entities considered to be
+     * non-abstract.
+     * <p>
+     * Declaring tokens as abstract is important to minimize number of parsing variants automatically
+     * generated as permutation of all possible parsing compositions. For example, if it is known that a particular
+     * named entity will only be used as a constituent part of some other token - declaring such named entity as
+     * abstract can significantly reduce the number of parsing variants leading to a better performance,
+     * and often simpler corresponding intent definition and callback logic.
+     *
+     * @return Set of abstract token IDs. Can be empty but never {@code null}.
+     */
+    Set<String> getAbstractTokens();
+
+    /**
+     * Gets maximum number of unique synonyms per model element after which either warning or error will be
+     * triggered. Note that there is no technical limit on how many synonyms a model element can have apart
+     * from memory consumption and performance considerations. However, in cases where synonyms are auto-generated
+     * (i.e. from database) this property can serve as a courtesy notification that a model element has too many
+     * synonyms. Also, in general, too many synonyms can potentially lead to a performance degradation.
+     *
+     * @return Maximum number of unique synonyms per model element after which either warning or
+     *      error will be triggered.
+     * @see #isMaxSynonymsThresholdError()
+     * @see #getMaxTotalSynonyms()
+     */
+    int getMaxElementSynonyms();
+
+    /**
+     * Whether exceeding {@link #getMaxElementSynonyms()} will trigger a warning log or throwing an exception.
+     * Note that throwing exception will prevent data probe from starting.
+     *
+     * @return Whether exceeding {@link #getMaxElementSynonyms()} will trigger a warning log or
+     *      throwing an exception.
+     * @see #getMaxElementSynonyms()
+     */
+    boolean isMaxSynonymsThresholdError();
+
+    /**
+     * Gets timeout in ms after which the unused conversation element is automatically "forgotten".
+     * <p>
+     * Just like in a normal human conversation if we talk about, say, "Chicago", and then don't mention it
+     * for certain period of time during further dialog, the conversation participants subconsciously "forget"
+     * about it and exclude it from conversation context. In other words, the term "Chicago" is no longer in
+     * conversation's short-term-memory.
+     * <p>
+     * Note that both conversation timeout and {@link #getConversationDepth() depth}
+     * combined define the expiration policy for the conversation management. These two properties allow fine-tuning
+     * for different types of dialogs. For example, setting longer timeout and smaller depth mimics
+     * slow-moving but topic-focused conversation. Alternatively, settings shorter timeout and longer depth better
+     * supports fast-moving wide-ranging conversation that may cover multiple topics.
+     *
+     * @return Timeout in ms after which the unused conversation element is automatically "forgotten".
+     * @see #getConversationDepth()
+     */
+    long getConversationTimeout();
+
+    /**
+     * Gets maximum number of requests after which the unused conversation element is automatically "forgotten".
+     * <p>
+     * Just like in a normal human conversation if we talk about, say, "Chicago", and then don't mention it
+     * for a certain number of utterances during further dialog, the conversation participants subconsciously "forget"
+     * about it and exclude it from conversation context. In other words, the term "Chicago" is no longer in
+     * conversation's short-term-memory.
+     * <p>
+     * Note that both conversation {@link #getConversationTimeout() timeout} and depth
+     * combined define the expiration policy for the conversation management. These two properties allow fine-tuning
+     * for different types of dialogs. For example, setting longer timeout and smaller depth mimics
+     * slow-moving but topic-focused conversation. Alternatively, settings shorter timeout and longer depth better
+     * supports fast-moving wide-ranging conversation that may cover multiple topics.
+     *
+     * @return Maximum number of requests after which the unused conversation element is automatically "forgotten".
+     * @see #getConversationTimeout()
+     */
+    int getConversationDepth();
+
+    /**
+     * Gets an optional map of restricted named entity combinations (linkage). Returned map is a map of entity ID to a set
+     * of other entity IDs, with each key-value pair defining the restricted combination. Restricting certain entities
+     * from being linked (or referenced) by some other entities allows reducing "wasteful" parsing variant
+     * generation. For example, if we know that entity with ID "adjective" cannot be sorted, we can restrict it
+     * from being linked with <code>nlpcraft:limit</code> and <code>nlpcraft:sort</code> entities to reduce the
+     * amount of parsing variants being generated.
+     * <p>
+     * Only the following built-in entities can be restricted (i.e., to be the keys in the returned map):
+     * <ul>
+     *     <li><code>nlpcraft:limit</code></li>
+     *     <li><code>nlpcraft:sort</code></li>
+     *     <li><code>nlpcraft:relation</code></li>
+     * </ul>
+     * Note that entity cannot be restricted to itself (entity ID cannot appear as key as well as a
+     * part of the value's set).
+     *
+     * @return Optional map of restricted named entity combinations. Can be empty but never {@code null}.
+     */
+    Map<String, Set<String>> getRestrictedCombinations();
+}
diff --git a/src/main/java/org/apache/nlpcraft/client/NCResult.java b/src/main/java/org/apache/nlpcraft/client/NCResult.java
index 802e890..74d9871 100644
--- a/src/main/java/org/apache/nlpcraft/client/NCResult.java
+++ b/src/main/java/org/apache/nlpcraft/client/NCResult.java
@@ -112,4 +112,11 @@
      * @return Optional processing log. Can be {@code null}.
      */
     String getLogHolder();
+
+    /**
+     * Gets optional metadata of the query result.
+     *
+     * @return Optional metadata of the query result.
+     */
+    Map<String, Object> getResultMeta();
 }
diff --git a/src/main/java/org/apache/nlpcraft/client/NCSuggestion.java b/src/main/java/org/apache/nlpcraft/client/NCSuggestion.java
index cb90f1c..dd53361 100644
--- a/src/main/java/org/apache/nlpcraft/client/NCSuggestion.java
+++ b/src/main/java/org/apache/nlpcraft/client/NCSuggestion.java
@@ -18,9 +18,10 @@
 package org.apache.nlpcraft.client;
 
 /**
- * Individual synonym suggestion returned from {@link NCSuggestionData#getSynonyms()} method.
+ * Individual synonym suggestion.
  *
  * @see <a href="https://nlpcraft.apache.org/tools/syn_tool.html">Synonym suggestion tool</a>.
+ * @see NCSuggestionData#getSynonyms()
  */
 public interface NCSuggestion {
     /**
diff --git a/src/main/java/org/apache/nlpcraft/client/NCSuggestionData.java b/src/main/java/org/apache/nlpcraft/client/NCSuggestionData.java
index 967b7f0..56c7e3c 100644
--- a/src/main/java/org/apache/nlpcraft/client/NCSuggestionData.java
+++ b/src/main/java/org/apache/nlpcraft/client/NCSuggestionData.java
@@ -21,9 +21,10 @@
 import java.util.Map;
 
 /**
- * Result container returned by {@link NCClient#suggestSynonyms(String, Double)} method.
+ * Synonym suggestion tool result descriptor.
  *
  * @see <a href="https://nlpcraft.apache.org/tools/syn_tool.html">Synonym suggestion tool</a>.
+ * @see NCClient#suggestSynonyms(String, Double)
  */
 public interface NCSuggestionData {
     /**
diff --git a/src/main/java/org/apache/nlpcraft/client/NCUser.java b/src/main/java/org/apache/nlpcraft/client/NCUser.java
index 14989b1..fb6242e 100644
--- a/src/main/java/org/apache/nlpcraft/client/NCUser.java
+++ b/src/main/java/org/apache/nlpcraft/client/NCUser.java
@@ -62,9 +62,9 @@
     String getAvatarUrl();
 
     /**
-     * Gets whether or not this user has administrative privileges.
+     * Gets whether this user has administrative privileges.
      *
-     * @return Whether or not this user has administrative privileges.
+     * @return Whether this user has administrative privileges.
      */
     boolean isAdmin();
     
@@ -80,5 +80,5 @@
      *
      * @return Optional user properties (metadata).
      */
-    Map<String, String> getProperties();
+    Map<String, Object> getProperties();
 }
diff --git a/src/main/java/org/apache/nlpcraft/client/NCValue.java b/src/main/java/org/apache/nlpcraft/client/NCValue.java
new file mode 100644
index 0000000..0b96595
--- /dev/null
+++ b/src/main/java/org/apache/nlpcraft/client/NCValue.java
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.nlpcraft.client;
+
+import java.util.List;
+
+/**
+ * Model element's value descriptor.
+ * <p>
+ * Each model element can generally be recognized either by one of its synonyms or values. Elements and their values
+ * are analogous to types and instances of that type in programming languages. Each value
+ * has a name and optional set of its own synonyms by which that value, and ultimately its element, can be
+ * recognized by. Note that value name itself acts as an implicit synonym even when no additional synonyms added
+ * for that value.
+ *
+ * @see NCElement#getValues()
+ */
+public interface NCValue {
+    /**
+     * Gets value name.
+     *
+     * @return Value name.
+     */
+    String getName();
+
+    /**
+     * Gets optional list of value's synonyms.
+     *
+     * @return Potentially empty list of value's synonyms.
+     */
+    List<String> getSynonyms();
+}
diff --git a/src/main/java/org/apache/nlpcraft/client/impl/NCClientImpl.java b/src/main/java/org/apache/nlpcraft/client/impl/NCClientImpl.java
index 1d7055b..9327c19 100644
--- a/src/main/java/org/apache/nlpcraft/client/impl/NCClientImpl.java
+++ b/src/main/java/org/apache/nlpcraft/client/impl/NCClientImpl.java
@@ -22,6 +22,7 @@
 import com.google.gson.JsonDeserializer;
 import com.google.gson.JsonElement;
 import com.google.gson.JsonObject;
+import com.google.gson.reflect.TypeToken;
 import org.apache.commons.lang3.tuple.Pair;
 import org.apache.http.HttpEntity;
 import org.apache.http.client.ResponseHandler;
@@ -35,7 +36,9 @@
 import org.apache.nlpcraft.client.NCClient;
 import org.apache.nlpcraft.client.NCClientException;
 import org.apache.nlpcraft.client.NCCompany;
+import org.apache.nlpcraft.client.NCElementSynonymsData;
 import org.apache.nlpcraft.client.NCFeedback;
+import org.apache.nlpcraft.client.NCModelInfo;
 import org.apache.nlpcraft.client.NCNewCompany;
 import org.apache.nlpcraft.client.NCProbe;
 import org.apache.nlpcraft.client.NCResult;
@@ -46,9 +49,11 @@
 import org.apache.nlpcraft.client.impl.beans.NCCheckBean;
 import org.apache.nlpcraft.client.impl.beans.NCCompanyBean;
 import org.apache.nlpcraft.client.impl.beans.NCCompanyTokenResetBean;
+import org.apache.nlpcraft.client.impl.beans.NCElementSynonymsDataBean;
 import org.apache.nlpcraft.client.impl.beans.NCErrorMessageBean;
 import org.apache.nlpcraft.client.impl.beans.NCFeedbackAddBean;
 import org.apache.nlpcraft.client.impl.beans.NCFeedbackAllBean;
+import org.apache.nlpcraft.client.impl.beans.NCModelInfoResultBean;
 import org.apache.nlpcraft.client.impl.beans.NCProbesAllBean;
 import org.apache.nlpcraft.client.impl.beans.NCRequestStateBean;
 import org.apache.nlpcraft.client.impl.beans.NCSigninBean;
@@ -58,8 +63,6 @@
 import org.apache.nlpcraft.client.impl.beans.NCUserAddBean;
 import org.apache.nlpcraft.client.impl.beans.NCUserBean;
 import org.apache.nlpcraft.client.impl.beans.NCUsersAllBean;
-import org.apache.nlpcraft.model.tools.embedded.NCEmbeddedProbe;
-import org.apache.nlpcraft.model.tools.embedded.NCEmbeddedResult;
 
 import java.io.IOException;
 import java.lang.reflect.Type;
@@ -68,8 +71,6 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.function.Consumer;
 import java.util.function.Function;
 import java.util.function.Supplier;
 import java.util.stream.Collectors;
@@ -80,32 +81,40 @@
 @SuppressWarnings("JavaDoc")
 public class NCClientImpl implements NCClient {
     private static final String STATUS_API_OK = "API_OK";
-    
+
+    private static final Type MAP_TYPE = new TypeToken<Map<String, Object>>(){}.getType();
+
+    private static final Gson gsonExt = new Gson();
     private static final Gson gson =
         new GsonBuilder().registerTypeAdapter(
             NCRequestStateBean.class,
             (JsonDeserializer<NCRequestStateBean>) (e, type, ctx) -> {
                 JsonObject o = e.getAsJsonObject();
                 NCRequestStateBean b = new NCRequestStateBean();
-    
+
                 b.setSrvReqId(o.get("srvReqId").getAsString());
                 b.setTxt(o.get("txt").getAsString());
                 b.setUsrId(o.get("usrId").getAsLong());
                 b.setMdlId(o.get("mdlId").getAsString());
                 b.setProbeId(convert(o, "probeId", JsonElement::getAsString));
-                b.setResType(convert(o, "resType", JsonElement::getAsString));
-                b.setResBody(
+                b.setResultType(convert(o, "resType", JsonElement::getAsString));
+                b.setResultBody(
                     convert(o, "resBody", (resBody) -> resBody == null ?
                         null :
                         resBody.isJsonObject() ?
                             resBody.getAsJsonObject().toString() :
                             resBody.getAsString())
                 );
+                b.setResultMeta(convert(o, "resMeta", e1 -> {
+                    Map<String, Object> m = gsonExt.fromJson(e1, MAP_TYPE);
+
+                    return m == null || m.isEmpty() ? null : m;
+                }));
                 b.setStatus(o.get("status").getAsString());
                 b.setErrorCode(convert(o, "errorCode", JsonElement::getAsInt));
                 b.setError(convert(o, "error", JsonElement::getAsString));
                 b.setLogHolder(convert(o, "logHolder", (logHolder) -> logHolder.getAsJsonObject().toString()));
-    
+
                 return b;
             }).create();
     
@@ -118,15 +127,11 @@
     private String email;
     private String pwd;
     private Boolean cancelOnExit;
-    private Boolean embeddedProbe;
-    
+
     private CloseableHttpClient httpCli;
     private String acsTok;
     private volatile boolean started = false;
     
-    private final Map<String, NCEmbeddedResult> embeddedResMap = new ConcurrentHashMap<>();
-    private final Object mux = new Object();
-    
     private static<T> T convert(JsonObject o, String name, Function<JsonElement, T> converter) {
         JsonElement e = o.get(name);
     
@@ -144,11 +149,6 @@
     }
 
     @Override
-    public boolean isClientEmbeddedMode() {
-        return embeddedProbe;
-    }
-
-    @Override
     public boolean isClientCancelOnExit() {
         return cancelOnExit;
     }
@@ -207,22 +207,6 @@
     }
 
     /**
-     * 
-     * @param embeddedProbe
-     */
-    public void setEmbeddedProbe(Boolean embeddedProbe) {
-        this.embeddedProbe = embeddedProbe;
-    }
-
-    /**
-     *
-     * @return
-     */
-    public Boolean isEmbeddedProbe() {
-        return embeddedProbe;
-    }
-    
-    /**
      *
      * @return
      */
@@ -281,18 +265,6 @@
         if (reqCfg == null)
             reqCfg = RequestConfig.DEFAULT;
     
-        if (embeddedProbe) {
-            Consumer<NCEmbeddedResult> embeddedCb = (NCEmbeddedResult res) -> {
-                embeddedResMap.put(res.getServerRequestId(), res);
-        
-                synchronized (mux) {
-                    mux.notifyAll();
-                }
-            };
-        
-            NCEmbeddedProbe.registerCallback(embeddedCb);
-        }
-        
         acsTok = restSignin();
         
         started = true;
@@ -408,7 +380,7 @@
 
                 if (code == 200)
                     return js;
-    
+
                 NCErrorMessageBean err;
                 
                 try {
@@ -417,7 +389,7 @@
                 catch (Exception e1) {
                     throw new NCClientException(String.format("Unexpected server error [code=%d]", code));
                 }
-    
+
                 throw new NCClientException(err.getMessage(), err.getCode());
             };
             
@@ -469,7 +441,7 @@
         String lastName,
         String avatarUrl,
         boolean isAdmin,
-        Map<String, String> properties,
+        Map<String, Object> properties,
         String extId
     ) throws NCClientException, IOException {
         notNull(email, "email");
@@ -514,7 +486,7 @@
     
     @Override
     public void updateUser(
-        long id, String firstName, String lastName, String avatarUrl, Map<String, String> properties
+        long id, String firstName, String lastName, String avatarUrl, Map<String, Object> properties
     ) throws NCClientException, IOException {
         notNull(firstName, "firstName");
         notNull(lastName, "lastName");
@@ -691,88 +663,6 @@
         notNull(mdlId, "mdlId");
         notNull(txt, "txt");
 
-        if (embeddedProbe) {
-            String srvReqId = ask(mdlId, txt, data, enableLog, usrId, usrExtId);
-            int timeout = reqCfg.getSocketTimeout();
-            long maxTime = System.currentTimeMillis() + timeout;
-
-            while (true) {
-                NCEmbeddedResult res = embeddedResMap.get(srvReqId);
-
-                if (res != null)
-                    return new NCResult() {
-                        @Override
-                        public String getServerRequestId() {
-                            return res.getServerRequestId();
-                        }
-
-                        @Override
-                        public String getText() {
-                            return res.getOriginalText();
-                        }
-
-                        @Override
-                        public long getUserId() {
-                            return res.getUserId();
-                        }
-
-                        @Override
-                        public String getModelId() {
-                            return res.getModelId();
-                        }
-
-                        @Override
-                        public String getProbeId() {
-                            return res.getProbeId();
-                        }
-
-                        @Override
-                        public boolean isReady() {
-                            return true;
-                        }
-
-                        @Override
-                        public String getResultType() {
-                            return res.getType();
-                        }
-
-                        @Override
-                        public String getResultBody() {
-                            return res.getBody();
-                        }
-
-                        @Override
-                        public Integer getErrorCode() {
-                            return res.getErrorCode() ==  0 ? null : res.getErrorCode();
-                        }
-
-                        @Override
-                        public String getErrorMessage() {
-                            return res.getErrorMessage();
-                        }
-
-                        @Override
-                        public String getLogHolder() {
-                            return res.getLogHolderJson();
-                        }
-                    };
-
-                long sleepTime = maxTime - System.currentTimeMillis();
-
-                if (sleepTime <= 0)
-                    throw new NCClientException(String.format("Request timeout: %d", timeout));
-
-                synchronized (mux) {
-                    try {
-                        mux.wait(sleepTime);
-                    }
-                    catch (InterruptedException e) {
-                        throw new NCClientException("Result wait thread interrupted.", e);
-                    }
-                }
-            }
-        }
-
         NCAskSyncBean b =
             checkAndExtract(
                 post(
@@ -882,7 +772,7 @@
     @Override
     public NCNewCompany addCompany(String name, String website, String country, String region, String city,
         String address, String postalCode, String adminEmail, String adminPasswd, String adminFirstName,
-        String adminLastName, String adminAvatarUrl, Map<String, String> props) throws IOException, NCClientException {
+        String adminLastName, String adminAvatarUrl, Map<String, Object> props) throws IOException, NCClientException {
         notNull(name, "name");
         notNull(adminEmail, "adminEmail");
         notNull(adminPasswd, "adminPasswd");
@@ -934,7 +824,7 @@
         String city,
         String address,
         String postalCode,
-        Map<String, String> props
+        Map<String, Object> props
     ) throws IOException, NCClientException {
         notNull(name, "name");
     
@@ -998,4 +888,31 @@
 
         return res.getResult();
     }
+
+    @Override
+    public NCElementSynonymsData getSynonyms(String mdlId, String elmId) throws NCClientException, IOException {
+        return checkAndExtract(
+            post(
+                "model/syns",
+                Pair.of("acsTok", acsTok),
+                Pair.of("mdlId", mdlId),
+                Pair.of("elmId", elmId)
+            ),
+            NCElementSynonymsDataBean.class
+        );
+    }
+
+    @Override
+    public NCModelInfo getModelInfo(String mdlId) throws NCClientException, IOException {
+        NCModelInfoResultBean res = checkAndExtract(
+            post(
+                "model/info",
+                Pair.of("acsTok", acsTok),
+                Pair.of("mdlId", mdlId)
+            ),
+            NCModelInfoResultBean.class
+        );
+
+        return res.getModel();
+    }
 }
diff --git a/src/main/java/org/apache/nlpcraft/client/impl/beans/NCAskBean.java b/src/main/java/org/apache/nlpcraft/client/impl/beans/NCAskBean.java
index b72cfd3..c5b6750 100644
--- a/src/main/java/org/apache/nlpcraft/client/impl/beans/NCAskBean.java
+++ b/src/main/java/org/apache/nlpcraft/client/impl/beans/NCAskBean.java
@@ -22,8 +22,9 @@
 /**
  * REST bean.
  */
-public class NCAskBean extends NCStatusResponseBean  {
-    @SerializedName("srvReqId") private String srvReqId;
+public class NCAskBean extends NCStatusResponseBean {
+    @SerializedName("srvReqId")
+    private String srvReqId;
 
     public String getServerRequestId() {
         return srvReqId;
diff --git a/src/main/java/org/apache/nlpcraft/client/impl/beans/NCAskSyncBean.java b/src/main/java/org/apache/nlpcraft/client/impl/beans/NCAskSyncBean.java
index aac9c33..0a461ea 100644
--- a/src/main/java/org/apache/nlpcraft/client/impl/beans/NCAskSyncBean.java
+++ b/src/main/java/org/apache/nlpcraft/client/impl/beans/NCAskSyncBean.java
@@ -22,8 +22,9 @@
 /**
  * REST bean.
  */
-public class NCAskSyncBean extends NCStatusResponseBean  {
-    @SerializedName("state") private NCRequestStateBean state;
+public class NCAskSyncBean extends NCStatusResponseBean {
+    @SerializedName("state")
+    private NCRequestStateBean state;
 
     public NCRequestStateBean getState() {
         return state;
diff --git a/src/main/java/org/apache/nlpcraft/client/impl/beans/NCCheckBean.java b/src/main/java/org/apache/nlpcraft/client/impl/beans/NCCheckBean.java
index 93a3a99..2df35ba 100644
--- a/src/main/java/org/apache/nlpcraft/client/impl/beans/NCCheckBean.java
+++ b/src/main/java/org/apache/nlpcraft/client/impl/beans/NCCheckBean.java
@@ -24,8 +24,9 @@
 /**
  * REST bean.
  */
-public class NCCheckBean extends NCStatusResponseBean  {
-    @SerializedName("states") private List<NCRequestStateBean> states;
+public class NCCheckBean extends NCStatusResponseBean {
+    @SerializedName("states")
+    private List<NCRequestStateBean> states;
 
     public List<NCRequestStateBean> getStates() {
         return states;
diff --git a/src/main/java/org/apache/nlpcraft/client/impl/beans/NCCompanyBean.java b/src/main/java/org/apache/nlpcraft/client/impl/beans/NCCompanyBean.java
index 9d3157e..a781bc3 100644
--- a/src/main/java/org/apache/nlpcraft/client/impl/beans/NCCompanyBean.java
+++ b/src/main/java/org/apache/nlpcraft/client/impl/beans/NCCompanyBean.java
@@ -26,41 +26,67 @@
  * REST bean.
  */
 public class NCCompanyBean extends NCStatusResponseBean implements NCCompany {
-    @SerializedName("id") private long id;
-    @SerializedName("name") private String name;
-    @SerializedName("website") private String website;
-    @SerializedName("country") private String country;
-    @SerializedName("region") private String region;
-    @SerializedName("city") private String city;
-    @SerializedName("address") private String address;
-    @SerializedName("postalCode") private String postalCode;
-    @SerializedName("properties") private Map<String, String> properties;
-    
-    @Override public long getId() {
+    @SerializedName("id")
+    private long id;
+    @SerializedName("name")
+    private String name;
+    @SerializedName("website")
+    private String website;
+    @SerializedName("country")
+    private String country;
+    @SerializedName("region")
+    private String region;
+    @SerializedName("city")
+    private String city;
+    @SerializedName("address")
+    private String address;
+    @SerializedName("postalCode")
+    private String postalCode;
+    @SerializedName("properties")
+    private Map<String, Object> properties;
+
+    @Override
+    public long getId() {
         return id;
     }
-    @Override public String getName() {
+
+    @Override
+    public String getName() {
         return name;
     }
-    @Override public String getWebsite() {
+
+    @Override
+    public String getWebsite() {
         return website;
     }
-    @Override public String getCountry() {
+
+    @Override
+    public String getCountry() {
         return country;
     }
-    @Override public String getRegion() {
+
+    @Override
+    public String getRegion() {
         return region;
     }
-    @Override public String getCity() {
+
+    @Override
+    public String getCity() {
         return city;
     }
-    @Override public String getAddress() {
+
+    @Override
+    public String getAddress() {
         return address;
     }
-    @Override public String getPostalCode() {
+
+    @Override
+    public String getPostalCode() {
         return postalCode;
     }
-    @Override public Map<String, String> getProperties() {
+
+    @Override
+    public Map<String, Object> getProperties() {
         return properties;
     }
 }
diff --git a/src/main/java/org/apache/nlpcraft/client/impl/beans/NCCompanyTokenResetBean.java b/src/main/java/org/apache/nlpcraft/client/impl/beans/NCCompanyTokenResetBean.java
index 5fed10d..73bbdb4 100644
--- a/src/main/java/org/apache/nlpcraft/client/impl/beans/NCCompanyTokenResetBean.java
+++ b/src/main/java/org/apache/nlpcraft/client/impl/beans/NCCompanyTokenResetBean.java
@@ -22,8 +22,9 @@
 /**
  * REST bean.
  */
-public class NCCompanyTokenResetBean extends NCStatusResponseBean  {
-    @SerializedName("token") private String token;
+public class NCCompanyTokenResetBean extends NCStatusResponseBean {
+    @SerializedName("token")
+    private String token;
 
     public String getToken() {
         return token;
diff --git a/src/main/java/org/apache/nlpcraft/client/impl/beans/NCElementBean.java b/src/main/java/org/apache/nlpcraft/client/impl/beans/NCElementBean.java
new file mode 100644
index 0000000..109b71c
--- /dev/null
+++ b/src/main/java/org/apache/nlpcraft/client/impl/beans/NCElementBean.java
@@ -0,0 +1,89 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.nlpcraft.client.impl.beans;
+
+import com.google.gson.annotations.SerializedName;
+import org.apache.nlpcraft.client.NCElement;
+import org.apache.nlpcraft.client.NCValue;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+/**
+ * REST bean.
+ */
+public class NCElementBean implements NCElement {
+    @SerializedName("id") private String id;
+    @SerializedName("groups") private List<String> groups;
+    @SerializedName("metadata") private Map<String, Object> metadata;
+    @SerializedName("description") private String description;
+    @SerializedName("values") private List<NCValueBean> values;
+    @SerializedName("parentId") private String parentId;
+    @SerializedName("synonyms") private List<String> synonyms;
+    // java.lang.Boolean but not boolean
+    @SerializedName("permutateSynonyms") private Boolean permutateSynonyms;
+    // java.lang.Boolean but not boolean
+    @SerializedName("sparse") private Boolean sparse;
+
+    @Override
+    public String getId() {
+        return id;
+    }
+
+    @Override
+    public List<String> getGroups() {
+        return groups;
+    }
+
+    @Override
+    public Map<String, Object> getMetadata() {
+        return metadata;
+    }
+
+    @Override
+    public String getDescription() {
+        return description;
+    }
+
+    @Override
+    public List<NCValue> getValues() {
+        return values != null ? new ArrayList<>(values) : null;
+    }
+
+    @Override
+    public String getParentId() {
+        return parentId;
+    }
+
+    @Override
+    public List<String> getSynonyms() {
+        return synonyms;
+    }
+
+    @Override
+    public Optional<Boolean> isPermutateSynonyms() {
+        return Optional.ofNullable(permutateSynonyms);
+    }
+
+    @Override
+    public Optional<Boolean> isSparse() {
+        return Optional.ofNullable(sparse);
+    }
+}
diff --git a/src/main/java/org/apache/nlpcraft/client/impl/beans/NCElementSynonymsDataBean.java b/src/main/java/org/apache/nlpcraft/client/impl/beans/NCElementSynonymsDataBean.java
new file mode 100644
index 0000000..c321acb
--- /dev/null
+++ b/src/main/java/org/apache/nlpcraft/client/impl/beans/NCElementSynonymsDataBean.java
@@ -0,0 +1,85 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.nlpcraft.client.impl.beans;
+
+import com.google.gson.annotations.SerializedName;
+import org.apache.nlpcraft.client.NCElementSynonymsData;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * REST bean.
+ */
+public class NCElementSynonymsDataBean extends NCStatusResponseBean implements NCElementSynonymsData {
+    @SerializedName("synonymsCnt")
+    private int synonymsCnt;
+
+    @SerializedName("synonymsExpCnt")
+    private int synonymsExpCnt;
+
+    @SerializedName("synonymsExpRatePct")
+    private long synonymsExpRatePct;
+
+    @SerializedName("synonyms")
+    private List<String> synonyms;
+
+    @SerializedName("synonymsExp")
+    private List<String> synonymsExp;
+
+    @SerializedName("values")
+    private Map<String, List<String>> values;
+
+    @SerializedName("valuesExp")
+    private Map<String, List<String>> valuesExp;
+
+    @Override
+    public int getSynonymsCnt() {
+        return synonymsCnt;
+    }
+
+    @Override
+    public int getSynonymsExpCnt() {
+        return synonymsExpCnt;
+    }
+
+    @Override
+    public long getSynonymsExpRatePct() {
+        return synonymsExpRatePct;
+    }
+
+    @Override
+    public List<String> getSynonyms() {
+        return synonyms;
+    }
+
+    @Override
+    public List<String> getSynonymsExp() {
+        return synonymsExp;
+    }
+
+    @Override
+    public Map<String, List<String>> getValues() {
+        return values;
+    }
+
+    @Override
+    public Map<String, List<String>> getValuesExp() {
+        return valuesExp;
+    }
+}
diff --git a/src/main/java/org/apache/nlpcraft/client/impl/beans/NCErrorMessageBean.java b/src/main/java/org/apache/nlpcraft/client/impl/beans/NCErrorMessageBean.java
index 73c0d79..d01ea94 100644
--- a/src/main/java/org/apache/nlpcraft/client/impl/beans/NCErrorMessageBean.java
+++ b/src/main/java/org/apache/nlpcraft/client/impl/beans/NCErrorMessageBean.java
@@ -23,12 +23,15 @@
  * REST bean.
  */
 public class NCErrorMessageBean {
-    @SerializedName("code") private String code;
-    @SerializedName("msg") private String msg;
+    @SerializedName("code")
+    private String code;
+    @SerializedName("msg")
+    private String msg;
 
     public String getCode() {
         return code;
     }
+
     public String getMessage() {
         return msg;
     }
diff --git a/src/main/java/org/apache/nlpcraft/client/impl/beans/NCFeedbackAddBean.java b/src/main/java/org/apache/nlpcraft/client/impl/beans/NCFeedbackAddBean.java
index d94d03b..a4fc1c7 100644
--- a/src/main/java/org/apache/nlpcraft/client/impl/beans/NCFeedbackAddBean.java
+++ b/src/main/java/org/apache/nlpcraft/client/impl/beans/NCFeedbackAddBean.java
@@ -22,8 +22,9 @@
 /**
  * REST bean.
  */
-public class NCFeedbackAddBean extends NCStatusResponseBean  {
-    @SerializedName("id") private Long id;
+public class NCFeedbackAddBean extends NCStatusResponseBean {
+    @SerializedName("id")
+    private Long id;
 
     public Long getId() {
         return id;
diff --git a/src/main/java/org/apache/nlpcraft/client/impl/beans/NCFeedbackAllBean.java b/src/main/java/org/apache/nlpcraft/client/impl/beans/NCFeedbackAllBean.java
index 91f753a..f2be781 100644
--- a/src/main/java/org/apache/nlpcraft/client/impl/beans/NCFeedbackAllBean.java
+++ b/src/main/java/org/apache/nlpcraft/client/impl/beans/NCFeedbackAllBean.java
@@ -24,8 +24,9 @@
 /**
  * REST bean.
  */
-public class NCFeedbackAllBean extends NCStatusResponseBean  {
-    @SerializedName("feedback") private List<NCFeedbackBean> feedback;
+public class NCFeedbackAllBean extends NCStatusResponseBean {
+    @SerializedName("feedback")
+    private List<NCFeedbackBean> feedback;
 
     public List<NCFeedbackBean> getFeedback() {
         return feedback;
diff --git a/src/main/java/org/apache/nlpcraft/client/impl/beans/NCFeedbackBean.java b/src/main/java/org/apache/nlpcraft/client/impl/beans/NCFeedbackBean.java
index 119ab65..aef0172 100644
--- a/src/main/java/org/apache/nlpcraft/client/impl/beans/NCFeedbackBean.java
+++ b/src/main/java/org/apache/nlpcraft/client/impl/beans/NCFeedbackBean.java
@@ -30,40 +30,56 @@
     private String comment;
     private long createTstamp;
 
-    @Override public String getServerRequestId() {
+    @Override
+    public String getServerRequestId() {
         return srvReqId;
     }
-    @Override public long getCreateTimestamp() {
+
+    public void setServerRequestId(String srvReqId) {
+        this.srvReqId = srvReqId;
+    }
+
+    @Override
+    public long getCreateTimestamp() {
         return createTstamp;
     }
-    @Override public long getUserId() {
+
+    @Override
+    public long getUserId() {
         return usrId;
     }
-    @Override public double getScore() {
+
+    @Override
+    public double getScore() {
         return score;
     }
-    @Override public String getComment() {
+
+    public void setScore(double score) {
+        this.score = score;
+    }
+
+    @Override
+    public String getComment() {
         return comment;
     }
-    @Override public long getId() {
+
+    public void setComment(String comment) {
+        this.comment = comment;
+    }
+
+    @Override
+    public long getId() {
         return id;
     }
 
     public void setId(long id) {
         this.id = id;
     }
-    public void setServerRequestId(String srvReqId) {
-        this.srvReqId = srvReqId;
-    }
+
     public void setUsrId(long usrId) {
         this.usrId = usrId;
     }
-    public void setScore(double score) {
-        this.score = score;
-    }
-    public void setComment(String comment) {
-        this.comment = comment;
-    }
+
     public void setCreateTstamp(long createTstamp) {
         this.createTstamp = createTstamp;
     }
diff --git a/src/main/java/org/apache/nlpcraft/client/impl/beans/NCModelBean.java b/src/main/java/org/apache/nlpcraft/client/impl/beans/NCModelBean.java
index 1fcf975..c2c74c5 100644
--- a/src/main/java/org/apache/nlpcraft/client/impl/beans/NCModelBean.java
+++ b/src/main/java/org/apache/nlpcraft/client/impl/beans/NCModelBean.java
@@ -24,17 +24,25 @@
  * REST bean.
  */
 public class NCModelBean implements NCModel {
-    @SerializedName("id") private String id;
-    @SerializedName("name") private String name;
-    @SerializedName("version") private String version;
-    
-    @Override public String getId() {
+    @SerializedName("id")
+    private String id;
+    @SerializedName("name")
+    private String name;
+    @SerializedName("version")
+    private String version;
+
+    @Override
+    public String getId() {
         return id;
     }
-    @Override public String getName() {
+
+    @Override
+    public String getName() {
         return name;
     }
-    @Override public String getVersion() {
+
+    @Override
+    public String getVersion() {
         return version;
     }
 }
\ No newline at end of file
diff --git a/src/main/java/org/apache/nlpcraft/client/impl/beans/NCModelInfoBean.java b/src/main/java/org/apache/nlpcraft/client/impl/beans/NCModelInfoBean.java
new file mode 100644
index 0000000..5797107
--- /dev/null
+++ b/src/main/java/org/apache/nlpcraft/client/impl/beans/NCModelInfoBean.java
@@ -0,0 +1,242 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.nlpcraft.client.impl.beans;
+
+import com.google.gson.annotations.SerializedName;
+import org.apache.nlpcraft.client.NCElement;
+import org.apache.nlpcraft.client.NCModelInfo;
+
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * REST bean.
+ */
+public class NCModelInfoBean implements NCModelInfo {
+    @SerializedName("id") private String id;
+    @SerializedName("name") private String name;
+    @SerializedName("version") private String version;
+    @SerializedName("description") private String description;
+    @SerializedName("origin") private String origin;
+    @SerializedName("maxUnknownWords") private int maxUnknownWords;
+    @SerializedName("maxFreeWords") private int maxFreeWords;
+    @SerializedName("maxSuspiciousWords") private int maxSuspiciousWords;
+    @SerializedName("minWords") private int minWords;
+    @SerializedName("maxWords") private int maxWords;
+    @SerializedName("minTokens") private int minTokens;
+    @SerializedName("maxTokens") private int maxTokens;
+    @SerializedName("minNonStopwords") private int minNonStopwords;
+    @SerializedName("nonEnglishAllowed") private boolean nonEnglishAllowed;
+    @SerializedName("notLatinCharsetAllowed") private boolean notLatinCharsetAllowed;
+    @SerializedName("swearWordsAllowed") private boolean swearWordsAllowed;
+    @SerializedName("noNounsAllowed") private boolean noNounsAllowed;
+    @SerializedName("permutateSynonyms") private boolean permutateSynonyms;
+    @SerializedName("dupSynonymsAllowed") private boolean dupSynonymsAllowed;
+    @SerializedName("maxTotalSynonyms") private int maxTotalSynonyms;
+    @SerializedName("noUserTokensAllowed") private boolean noUserTokensAllowed;
+    @SerializedName("sparse") private boolean sparse;
+    @SerializedName("metadata") private Map<String, Object> metadata;
+    @SerializedName("additionalStopWords") private Set<String> additionalStopWords;
+    @SerializedName("excludedStopWords") private Set<String> excludedStopWords;
+    @SerializedName("suspiciousWords") private Set<String> suspiciousWords;
+    @SerializedName("macros") private Map<String, String> macros;
+    @SerializedName("elements") private Set<NCElementBean> elements;
+    @SerializedName("enabledBuiltInTokens") private Set<String> enabledBuiltInTokens;
+    @SerializedName("abstractTokens") private Set<String> abstractTokens;
+    @SerializedName("maxElementSynonyms") private int maxElementSynonyms;
+    @SerializedName("maxSynonymsThresholdError") private boolean maxSynonymsThresholdError;
+    @SerializedName("conversationTimeout") private long conversationTimeout;
+    @SerializedName("conversationDepth") private int conversationDepth;
+    @SerializedName("restrictedCombinations") private Map<String, Set<String>> restrictedCombinations;
+
+    @Override
+    public String getId() {
+        return id;
+    }
+
+    @Override
+    public String getName() {
+        return name;
+    }
+
+    @Override
+    public String getVersion() {
+        return version;
+    }
+
+    @Override
+    public String getDescription() {
+        return description;
+    }
+
+    @Override
+    public String getOrigin() {
+        return origin;
+    }
+
+    @Override
+    public int getMaxUnknownWords() {
+        return maxUnknownWords;
+    }
+
+    @Override
+    public int getMaxFreeWords() {
+        return maxFreeWords;
+    }
+
+    @Override
+    public int getMaxSuspiciousWords() {
+        return maxSuspiciousWords;
+    }
+
+    @Override
+    public int getMinWords() {
+        return minWords;
+    }
+
+    @Override
+    public int getMaxWords() {
+        return maxWords;
+    }
+
+    @Override
+    public int getMinTokens() {
+        return minTokens;
+    }
+
+    @Override
+    public int getMaxTokens() {
+        return maxTokens;
+    }
+
+    @Override
+    public int getMinNonStopwords() {
+        return minNonStopwords;
+    }
+
+    @Override
+    public boolean isNonEnglishAllowed() {
+        return nonEnglishAllowed;
+    }
+
+    @Override
+    public boolean isNotLatinCharsetAllowed() {
+        return notLatinCharsetAllowed;
+    }
+
+    @Override
+    public boolean isSwearWordsAllowed() {
+        return swearWordsAllowed;
+    }
+
+    @Override
+    public boolean isNoNounsAllowed() {
+        return noNounsAllowed;
+    }
+
+    @Override
+    public boolean isPermutateSynonyms() {
+        return permutateSynonyms;
+    }
+
+    @Override
+    public boolean isDupSynonymsAllowed() {
+        return dupSynonymsAllowed;
+    }
+
+    @Override
+    public int getMaxTotalSynonyms() {
+        return maxTotalSynonyms;
+    }
+
+    @Override
+    public boolean isNoUserTokensAllowed() {
+        return noUserTokensAllowed;
+    }
+
+    @Override
+    public boolean isSparse() {
+        return sparse;
+    }
+
+    @Override
+    public Map<String, Object> getMetadata() {
+        return metadata;
+    }
+
+    @Override
+    public Set<String> getAdditionalStopWords() {
+        return additionalStopWords;
+    }
+
+    @Override
+    public Set<String> getExcludedStopWords() {
+        return excludedStopWords;
+    }
+
+    @Override
+    public Set<String> getSuspiciousWords() {
+        return suspiciousWords;
+    }
+
+    @Override
+    public Map<String, String> getMacros() {
+        return macros;
+    }
+
+    @Override
+    public Set<NCElement> getElements() {
+        return elements != null ? new HashSet<>(elements) : null;
+    }
+
+    @Override
+    public Set<String> getEnabledBuiltInTokens() {
+        return enabledBuiltInTokens;
+    }
+
+    @Override
+    public Set<String> getAbstractTokens() {
+        return abstractTokens;
+    }
+
+    @Override
+    public int getMaxElementSynonyms() {
+        return maxElementSynonyms;
+    }
+
+    @Override
+    public boolean isMaxSynonymsThresholdError() {
+        return maxSynonymsThresholdError;
+    }
+
+    @Override
+    public long getConversationTimeout() {
+        return conversationTimeout;
+    }
+
+    @Override
+    public int getConversationDepth() {
+        return conversationDepth;
+    }
+
+    @Override
+    public Map<String, Set<String>> getRestrictedCombinations() {
+        return restrictedCombinations;
+    }
+}
diff --git a/src/main/java/org/apache/nlpcraft/client/impl/beans/NCModelInfoResultBean.java b/src/main/java/org/apache/nlpcraft/client/impl/beans/NCModelInfoResultBean.java
new file mode 100644
index 0000000..0750e9d
--- /dev/null
+++ b/src/main/java/org/apache/nlpcraft/client/impl/beans/NCModelInfoResultBean.java
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.nlpcraft.client.impl.beans;
+
+import com.google.gson.annotations.SerializedName;
+
+/**
+ * REST bean.
+ */
+public class NCModelInfoResultBean extends NCStatusResponseBean {
+    @SerializedName("model")
+    private NCModelInfoBean model;
+
+    public NCModelInfoBean getModel() {
+        return model;
+    }
+}
diff --git a/src/main/java/org/apache/nlpcraft/client/impl/beans/NCProbeBean.java b/src/main/java/org/apache/nlpcraft/client/impl/beans/NCProbeBean.java
index 86f9dda..88a191d 100644
--- a/src/main/java/org/apache/nlpcraft/client/impl/beans/NCProbeBean.java
+++ b/src/main/java/org/apache/nlpcraft/client/impl/beans/NCProbeBean.java
@@ -29,43 +29,137 @@
  * REST bean.
  */
 public class NCProbeBean implements NCProbe {
-    @SerializedName("probeId") private String id;
-    @SerializedName("probeToken") private String probeToken;
-    @SerializedName("probeGuid") private String probeGuid;
-    @SerializedName("probeApiVersion") private String probeApiVersion;
-    @SerializedName("probeApiDate") private String probeApiDate;
-    @SerializedName("osVersion") private String osVersion;
-    @SerializedName("osName") private String osName;
-    @SerializedName("osArch") private String osArch;
-    @SerializedName("startTstamp") private long startTstamp;
-    @SerializedName("tmzId") private String tmzId;
-    @SerializedName("tmzAbbr") private String tmzAbbr;
-    @SerializedName("tmzName") private String tmzName;
-    @SerializedName("userName") private String userName;
-    @SerializedName("javaVersion") private String javaVersion;
-    @SerializedName("javaVendor") private String javaVendor;
-    @SerializedName("hostName") private String hostName;
-    @SerializedName("hostAddr") private String hostAddr;
-    @SerializedName("macAddr") private String macAddr;
-    @SerializedName("models") private Set<NCModelBean> models;
-    
-    @Override public String getProbeToken() { return probeToken; }
-    @Override public String getId() { return id; }
-    @Override public String getProbeGuid() { return probeGuid; }
-    @Override public String getProbeApiVersion() { return probeApiVersion; }
-    @Override public String getProbeApiDate() { return probeApiDate; }
-    @Override public String getOsVersion() { return osVersion; }
-    @Override public String getOsName() { return osName; }
-    @Override public String getOsArchitecture() { return osArch; }
-    @Override public long getStartTimestamp() { return startTstamp; }
-    @Override public String getTimezoneId() { return tmzId; }
-    @Override public String getTimezoneAbbreviation() { return tmzAbbr; }
-    @Override public String getTimezoneName() { return tmzName; }
-    @Override public String getUserName() { return userName; }
-    @Override public String getJavaVersion() { return javaVersion; }
-    @Override public String getJavaVendor() { return javaVendor; }
-    @Override public String getHostName() { return hostName; }
-    @Override public String getHostAddress() { return hostAddr; }
-    @Override public String getMacAddress() { return macAddr; }
-    @Override public Set<NCModel> getModels() { return models != null ? new HashSet<>(models) : Collections.emptySet(); }
+    @SerializedName("probeId")
+    private String id;
+    @SerializedName("probeToken")
+    private String probeToken;
+    @SerializedName("probeGuid")
+    private String probeGuid;
+    @SerializedName("probeApiVersion")
+    private String probeApiVersion;
+    @SerializedName("probeApiDate")
+    private String probeApiDate;
+    @SerializedName("osVersion")
+    private String osVersion;
+    @SerializedName("osName")
+    private String osName;
+    @SerializedName("osArch")
+    private String osArch;
+    @SerializedName("startTstamp")
+    private long startTstamp;
+    @SerializedName("tmzId")
+    private String tmzId;
+    @SerializedName("tmzAbbr")
+    private String tmzAbbr;
+    @SerializedName("tmzName")
+    private String tmzName;
+    @SerializedName("userName")
+    private String userName;
+    @SerializedName("javaVersion")
+    private String javaVersion;
+    @SerializedName("javaVendor")
+    private String javaVendor;
+    @SerializedName("hostName")
+    private String hostName;
+    @SerializedName("hostAddr")
+    private String hostAddr;
+    @SerializedName("macAddr")
+    private String macAddr;
+    @SerializedName("models")
+    private Set<NCModelBean> models;
+
+    @Override
+    public String getProbeToken() {
+        return probeToken;
+    }
+
+    @Override
+    public String getId() {
+        return id;
+    }
+
+    @Override
+    public String getProbeGuid() {
+        return probeGuid;
+    }
+
+    @Override
+    public String getProbeApiVersion() {
+        return probeApiVersion;
+    }
+
+    @Override
+    public String getProbeApiDate() {
+        return probeApiDate;
+    }
+
+    @Override
+    public String getOsVersion() {
+        return osVersion;
+    }
+
+    @Override
+    public String getOsName() {
+        return osName;
+    }
+
+    @Override
+    public String getOsArchitecture() {
+        return osArch;
+    }
+
+    @Override
+    public long getStartTimestamp() {
+        return startTstamp;
+    }
+
+    @Override
+    public String getTimezoneId() {
+        return tmzId;
+    }
+
+    @Override
+    public String getTimezoneAbbreviation() {
+        return tmzAbbr;
+    }
+
+    @Override
+    public String getTimezoneName() {
+        return tmzName;
+    }
+
+    @Override
+    public String getUserName() {
+        return userName;
+    }
+
+    @Override
+    public String getJavaVersion() {
+        return javaVersion;
+    }
+
+    @Override
+    public String getJavaVendor() {
+        return javaVendor;
+    }
+
+    @Override
+    public String getHostName() {
+        return hostName;
+    }
+
+    @Override
+    public String getHostAddress() {
+        return hostAddr;
+    }
+
+    @Override
+    public String getMacAddress() {
+        return macAddr;
+    }
+
+    @Override
+    public Set<NCModel> getModels() {
+        return models != null ? new HashSet<>(models) : Collections.emptySet();
+    }
 }
diff --git a/src/main/java/org/apache/nlpcraft/client/impl/beans/NCProbesAllBean.java b/src/main/java/org/apache/nlpcraft/client/impl/beans/NCProbesAllBean.java
index e9a9f64..622577b 100644
--- a/src/main/java/org/apache/nlpcraft/client/impl/beans/NCProbesAllBean.java
+++ b/src/main/java/org/apache/nlpcraft/client/impl/beans/NCProbesAllBean.java
@@ -24,8 +24,9 @@
 /**
  * REST bean.
  */
-public class NCProbesAllBean extends NCStatusResponseBean  {
-    @SerializedName("probes") private List<NCProbeBean> probes;
+public class NCProbesAllBean extends NCStatusResponseBean {
+    @SerializedName("probes")
+    private List<NCProbeBean> probes;
 
     public List<NCProbeBean> getProbes() {
         return probes;
diff --git a/src/main/java/org/apache/nlpcraft/client/impl/beans/NCRequestStateBean.java b/src/main/java/org/apache/nlpcraft/client/impl/beans/NCRequestStateBean.java
index f50f9cf..1adfec6 100644
--- a/src/main/java/org/apache/nlpcraft/client/impl/beans/NCRequestStateBean.java
+++ b/src/main/java/org/apache/nlpcraft/client/impl/beans/NCRequestStateBean.java
@@ -19,6 +19,8 @@
 
 import org.apache.nlpcraft.client.NCResult;
 
+import java.util.Map;
+
 /**
  * REST bean.
  */
@@ -34,72 +36,113 @@
     private Integer errorCode;
     private String error;
     private String logHolder;
-    
-    @Override public String getServerRequestId() {
+    private Map<String, Object> resMeta;
+
+    @Override
+    public String getServerRequestId() {
         return srvReqId;
     }
-    @Override public String getText() {
-    return txt;
-}
-    @Override public long getUserId() {
-    return usrId;
-}
-    @Override public String getModelId() {
-    return mdlId;
-}
-    @Override public String getProbeId() {
-    return probeId;
-}
-    @Override public String getResultType() {
-    return resType;
-}
-    @Override public String getResultBody() {
-    return resBody;
-}
-    @Override public boolean isReady() {
-    return "QRY_READY".equals(status);
-}
-    @Override public Integer getErrorCode() {
-    return errorCode;
-}
-    @Override public String getErrorMessage() {
-    return error;
-}
-    @Override public String getLogHolder() {
-        return logHolder;
+
+    @Override
+    public String getText() {
+        return txt;
     }
-    
-    public void setSrvReqId(String srvReqId) {
-        this.srvReqId = srvReqId;
+
+    @Override
+    public long getUserId() {
+        return usrId;
     }
-    public void setTxt(String txt) {
-        this.txt = txt;
+
+    @Override
+    public String getModelId() {
+        return mdlId;
     }
-    public void setUsrId(long usrId) {
-        this.usrId = usrId;
+
+    @Override
+    public String getProbeId() {
+        return probeId;
     }
-    public void setMdlId(String mdlId) {
-        this.mdlId = mdlId;
-    }
+
     public void setProbeId(String probeId) {
         this.probeId = probeId;
     }
-    public void setResType(String resType) {
+
+    @Override
+    public String getResultType() {
+        return resType;
+    }
+
+    public void setResultType(String resType) {
         this.resType = resType;
     }
-    public void setResBody(String resBody) {
+
+    @Override
+    public String getResultBody() {
+        return resBody;
+    }
+
+    public void setResultBody(String resBody) {
         this.resBody = resBody;
     }
-    public void setStatus(String status) {
-        this.status = status;
+
+    @Override
+    public boolean isReady() {
+        return "QRY_READY".equals(status);
     }
+
+    @Override
+    public Integer getErrorCode() {
+        return errorCode;
+    }
+
     public void setErrorCode(Integer errorCode) {
         this.errorCode = errorCode;
     }
-    public void setError(String error) {
-        this.error = error;
+
+    @Override
+    public String getErrorMessage() {
+        return error;
     }
+
+    @Override
+    public String getLogHolder() {
+        return logHolder;
+    }
+
     public void setLogHolder(String logHolder) {
         this.logHolder = logHolder;
     }
+
+    @Override
+    public Map<String, Object> getResultMeta() {
+        return resMeta;
+    }
+
+    public void setResultMeta(Map<String, Object> resMeta) {
+        this.resMeta = resMeta;
+    }
+
+    public void setSrvReqId(String srvReqId) {
+        this.srvReqId = srvReqId;
+    }
+
+    public void setTxt(String txt) {
+        this.txt = txt;
+    }
+
+    public void setUsrId(long usrId) {
+        this.usrId = usrId;
+    }
+
+    public void setMdlId(String mdlId) {
+        this.mdlId = mdlId;
+    }
+
+    public void setStatus(String status) {
+        this.status = status;
+    }
+
+    public void setError(String error) {
+        this.error = error;
+    }
 }
diff --git a/src/main/java/org/apache/nlpcraft/client/impl/beans/NCSigninBean.java b/src/main/java/org/apache/nlpcraft/client/impl/beans/NCSigninBean.java
index 1051b85..1779deb 100644
--- a/src/main/java/org/apache/nlpcraft/client/impl/beans/NCSigninBean.java
+++ b/src/main/java/org/apache/nlpcraft/client/impl/beans/NCSigninBean.java
@@ -22,8 +22,9 @@
 /**
  * REST bean.
  */
-public class NCSigninBean extends NCStatusResponseBean  {
-    @SerializedName("acsTok") private String acsTok;
+public class NCSigninBean extends NCStatusResponseBean {
+    @SerializedName("acsTok")
+    private String acsTok;
 
     public String getAccessToken() {
         return acsTok;
diff --git a/src/main/java/org/apache/nlpcraft/client/impl/beans/NCStatusResponseBean.java b/src/main/java/org/apache/nlpcraft/client/impl/beans/NCStatusResponseBean.java
index c3ac263..f6c44d1 100644
--- a/src/main/java/org/apache/nlpcraft/client/impl/beans/NCStatusResponseBean.java
+++ b/src/main/java/org/apache/nlpcraft/client/impl/beans/NCStatusResponseBean.java
@@ -23,7 +23,8 @@
  * REST bean.
  */
 public class NCStatusResponseBean {
-    @SerializedName("status") private String status;
+    @SerializedName("status")
+    private String status;
 
     public String getStatus() {
         return status;
diff --git a/src/main/java/org/apache/nlpcraft/client/impl/beans/NCSuggestionBean.java b/src/main/java/org/apache/nlpcraft/client/impl/beans/NCSuggestionBean.java
index 943bfde..2130517 100644
--- a/src/main/java/org/apache/nlpcraft/client/impl/beans/NCSuggestionBean.java
+++ b/src/main/java/org/apache/nlpcraft/client/impl/beans/NCSuggestionBean.java
@@ -24,13 +24,18 @@
  * REST bean.
  */
 public class NCSuggestionBean implements NCSuggestion {
-    @SerializedName("score") private double score;
-    @SerializedName("synonym") private String synonym;
+    @SerializedName("score")
+    private double score;
+    @SerializedName("synonym")
+    private String synonym;
 
-    @Override public double getScore() {
+    @Override
+    public double getScore() {
         return score;
     }
-    @Override public String getSynonym() {
+
+    @Override
+    public String getSynonym() {
         return synonym;
     }
 }
diff --git a/src/main/java/org/apache/nlpcraft/client/impl/beans/NCSuggestionDataBean.java b/src/main/java/org/apache/nlpcraft/client/impl/beans/NCSuggestionDataBean.java
index 7804d1a..6321dcc 100644
--- a/src/main/java/org/apache/nlpcraft/client/impl/beans/NCSuggestionDataBean.java
+++ b/src/main/java/org/apache/nlpcraft/client/impl/beans/NCSuggestionDataBean.java
@@ -30,26 +30,50 @@
  * REST bean.
  */
 public class NCSuggestionDataBean implements NCSuggestionData {
-    @SerializedName("modelId") private String modelId;
-    @SerializedName("minScore") private double minScore;
-    @SerializedName("durationMs") private long durationMs;
-    @SerializedName("timestamp") private long timestamp;
-    @SerializedName("error") private String error;
-    @SerializedName("suggestions") private List<Map<String, List<NCSuggestionBean>>> suggestions;
-    @SerializedName("warnings") private java.util.List<String> warnings;
+    @SerializedName("modelId")
+    private String modelId;
+    @SerializedName("minScore")
+    private double minScore;
+    @SerializedName("durationMs")
+    private long durationMs;
+    @SerializedName("timestamp")
+    private long timestamp;
+    @SerializedName("error")
+    private String error;
+    @SerializedName("suggestions")
+    private List<Map<String, List<NCSuggestionBean>>> suggestions;
+    @SerializedName("warnings")
+    private java.util.List<String> warnings;
 
-    @Override public List<String> getWarnings() { return warnings; }
-    @Override public String getModelId() {
+    @Override
+    public List<String> getWarnings() {
+        return warnings;
+    }
+
+    @Override
+    public String getModelId() {
         return modelId;
     }
-    @Override public double getMinScore() {
+
+    @Override
+    public double getMinScore() {
         return minScore;
     }
-    @Override public long getDurationMs() {
+
+    @Override
+    public long getDurationMs() {
         return durationMs;
     }
-    @Override public long getTimestamp() { return timestamp; }
-    @Override public String getError() { return error; }
+
+    @Override
+    public long getTimestamp() {
+        return timestamp;
+    }
+
+    @Override
+    public String getError() {
+        return error;
+    }
 
     @Override
     public List<Map<String, List<NCSuggestion>>> getSynonyms() {
diff --git a/src/main/java/org/apache/nlpcraft/client/impl/beans/NCSuggestionResultBean.java b/src/main/java/org/apache/nlpcraft/client/impl/beans/NCSuggestionResultBean.java
index 304bb39..67a1fb2 100644
--- a/src/main/java/org/apache/nlpcraft/client/impl/beans/NCSuggestionResultBean.java
+++ b/src/main/java/org/apache/nlpcraft/client/impl/beans/NCSuggestionResultBean.java
@@ -22,8 +22,9 @@
 /**
  * REST bean.
  */
-public class NCSuggestionResultBean extends NCStatusResponseBean  {
-    @SerializedName("result") private NCSuggestionDataBean result;
+public class NCSuggestionResultBean extends NCStatusResponseBean {
+    @SerializedName("result")
+    private NCSuggestionDataBean result;
 
     public NCSuggestionDataBean getResult() {
         return result;
diff --git a/src/main/java/org/apache/nlpcraft/client/impl/beans/NCTokenCreationBean.java b/src/main/java/org/apache/nlpcraft/client/impl/beans/NCTokenCreationBean.java
index fb1dc78..4f2666b 100644
--- a/src/main/java/org/apache/nlpcraft/client/impl/beans/NCTokenCreationBean.java
+++ b/src/main/java/org/apache/nlpcraft/client/impl/beans/NCTokenCreationBean.java
@@ -24,13 +24,18 @@
  * REST bean.
  */
 public class NCTokenCreationBean extends NCStatusResponseBean implements NCNewCompany {
-    @SerializedName("token") private String token;
-    @SerializedName("adminId") private long adminId;
-    
-    @Override public String getToken() {
+    @SerializedName("token")
+    private String token;
+    @SerializedName("adminId")
+    private long adminId;
+
+    @Override
+    public String getToken() {
         return token;
     }
-    @Override public long getAdminUserId() {
+
+    @Override
+    public long getAdminUserId() {
         return adminId;
     }
 }
diff --git a/src/main/java/org/apache/nlpcraft/client/impl/beans/NCUserAddBean.java b/src/main/java/org/apache/nlpcraft/client/impl/beans/NCUserAddBean.java
index e6086e3..a4df0e2 100644
--- a/src/main/java/org/apache/nlpcraft/client/impl/beans/NCUserAddBean.java
+++ b/src/main/java/org/apache/nlpcraft/client/impl/beans/NCUserAddBean.java
@@ -22,8 +22,9 @@
 /**
  * REST bean.
  */
-public class NCUserAddBean extends NCStatusResponseBean  {
-    @SerializedName("id") private long id;
+public class NCUserAddBean extends NCStatusResponseBean {
+    @SerializedName("id")
+    private long id;
 
     public long getId() {
         return id;
diff --git a/src/main/java/org/apache/nlpcraft/client/impl/beans/NCUserBean.java b/src/main/java/org/apache/nlpcraft/client/impl/beans/NCUserBean.java
index 4f9b5df..31c88c5 100644
--- a/src/main/java/org/apache/nlpcraft/client/impl/beans/NCUserBean.java
+++ b/src/main/java/org/apache/nlpcraft/client/impl/beans/NCUserBean.java
@@ -26,37 +26,60 @@
  * REST bean.
  */
 public class NCUserBean extends NCStatusResponseBean implements NCUser {
-    @SerializedName("id") private long id;
-    @SerializedName("usrExtId") private String usrExtId;
-    @SerializedName("email") private String email;
-    @SerializedName("firstName") private String firstName;
-    @SerializedName("lastName") private String lastName;
-    @SerializedName("avatarUrl") private String avatarUrl;
-    @SerializedName("isAdmin") private boolean isAdmin;
-    @SerializedName("properties") private Map<String, String> properties;
-    
-    @Override public long getId() {
+    @SerializedName("id")
+    private long id;
+    @SerializedName("usrExtId")
+    private String usrExtId;
+    @SerializedName("email")
+    private String email;
+    @SerializedName("firstName")
+    private String firstName;
+    @SerializedName("lastName")
+    private String lastName;
+    @SerializedName("avatarUrl")
+    private String avatarUrl;
+    @SerializedName("isAdmin")
+    private boolean isAdmin;
+    @SerializedName("properties")
+    private Map<String, Object> properties;
+
+    @Override
+    public long getId() {
         return id;
     }
-    @Override public String getEmail() {
+
+    @Override
+    public String getEmail() {
         return email;
     }
-    @Override public String getFirstName() {
-    return firstName;
-}
-    @Override public String getLastName() {
-    return lastName;
-}
-    @Override public String getAvatarUrl() {
-    return avatarUrl;
-}
-    @Override public boolean isAdmin() {
-    return isAdmin;
-}
-    @Override public String getExternalId() {
-    return usrExtId;
-}
-    @Override public Map<String, String> getProperties() {
+
+    @Override
+    public String getFirstName() {
+        return firstName;
+    }
+
+    @Override
+    public String getLastName() {
+        return lastName;
+    }
+
+    @Override
+    public String getAvatarUrl() {
+        return avatarUrl;
+    }
+
+    @Override
+    public boolean isAdmin() {
+        return isAdmin;
+    }
+
+    @Override
+    public String getExternalId() {
+        return usrExtId;
+    }
+
+    @Override
+    public Map<String, Object> getProperties() {
         return properties;
     }
 }
diff --git a/src/main/java/org/apache/nlpcraft/client/impl/beans/NCUsersAllBean.java b/src/main/java/org/apache/nlpcraft/client/impl/beans/NCUsersAllBean.java
index 3808dd3..62b525b 100644
--- a/src/main/java/org/apache/nlpcraft/client/impl/beans/NCUsersAllBean.java
+++ b/src/main/java/org/apache/nlpcraft/client/impl/beans/NCUsersAllBean.java
@@ -24,8 +24,9 @@
 /**
  * REST bean.
  */
-public class NCUsersAllBean extends NCStatusResponseBean  {
-    @SerializedName("users") private List<NCUserBean> users;
+public class NCUsersAllBean extends NCStatusResponseBean {
+    @SerializedName("users")
+    private List<NCUserBean> users;
 
     public List<NCUserBean> getUsers() {
         return users;
diff --git a/src/main/java/org/apache/nlpcraft/client/impl/beans/NCValueBean.java b/src/main/java/org/apache/nlpcraft/client/impl/beans/NCValueBean.java
new file mode 100644
index 0000000..2d4880f
--- /dev/null
+++ b/src/main/java/org/apache/nlpcraft/client/impl/beans/NCValueBean.java
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.nlpcraft.client.impl.beans;
+
+import com.google.gson.annotations.SerializedName;
+import org.apache.nlpcraft.client.NCValue;
+
+import java.util.List;
+
+/**
+ * REST bean.
+ */
+public class NCValueBean implements NCValue {
+    @SerializedName("name") private String name;
+    @SerializedName("synonyms") private List<String> synonyms;
+
+    @Override
+    public String getName() {
+        return name;
+    }
+
+    @Override
+    public List<String> getSynonyms() {
+        return synonyms;
+    }
+}
diff --git a/src/test/java/org/apache/nlpcraft/client/NCAskSyncTest.java b/src/test/java/org/apache/nlpcraft/client/NCAskSyncTest.java
index 8bef883..3a1050b 100644
--- a/src/test/java/org/apache/nlpcraft/client/NCAskSyncTest.java
+++ b/src/test/java/org/apache/nlpcraft/client/NCAskSyncTest.java
@@ -21,8 +21,6 @@
 import org.apache.nlpcraft.model.NCModel;
 import org.junit.jupiter.api.Test;
 
-import java.util.HashMap;
-import java.util.Map;
 import java.util.Optional;
 import java.util.function.Consumer;
 
@@ -46,22 +44,17 @@
         // Different combinations of input parameters.
         resConsumer.accept(admCli.askSync(MDL_ID, txt, null, true, null, null));
 
-        Map<String, Object> data = new HashMap<>();
-
-        data.put("a", 1);
-        data.put("b", "b1");
-
-        resConsumer.accept(admCli.askSync(MDL_ID, txt, data, true, null, null));
-        resConsumer.accept(admCli.askSync(MDL_ID, txt, data, false, null, null));
-        resConsumer.accept(admCli.askSync(MDL_ID, txt, data, false, admUsrId, null));
+        resConsumer.accept(admCli.askSync(MDL_ID, txt, MAP, true, null, null));
+        resConsumer.accept(admCli.askSync(MDL_ID, txt, MAP, false, null, null));
+        resConsumer.accept(admCli.askSync(MDL_ID, txt, MAP, false, admUsrId, null));
         
         String extId = "extId";
     
-        resConsumer.accept(admCli.askSync(MDL_ID, txt, data, false, null, extId));
+        resConsumer.accept(admCli.askSync(MDL_ID, txt, MAP, false, null, extId));
     
         long id = get(admCli.getAllUsers(), (u) -> extId.equals(u.getExternalId())).getId();
     
-        resConsumer.accept(admCli.askSync(MDL_ID, txt, data, false, id, extId));
+        resConsumer.accept(admCli.askSync(MDL_ID, txt, MAP, false, id, extId));
     }
     
     /**
@@ -73,6 +66,7 @@
         // Only latin charset is supported.
         check("El tiempo en España", this::checkError);
 
-        check("test", this::checkOk);
+        check("test", res -> checkOk(res, null));
+        check("meta", res -> checkOk(res, NCCommonSpecModel.MAP));
     }
 }
diff --git a/src/test/java/org/apache/nlpcraft/client/NCAskTest.java b/src/test/java/org/apache/nlpcraft/client/NCAskTest.java
index 78e0d1a..8a97a5d 100644
--- a/src/test/java/org/apache/nlpcraft/client/NCAskTest.java
+++ b/src/test/java/org/apache/nlpcraft/client/NCAskTest.java
@@ -83,7 +83,8 @@
      */
     @Test
     void test() throws Exception {
-        test0("test", this::checkOk);
+        test0("test", res -> checkOk(res, null));
+        test0("meta", res -> checkOk(res, NCCommonSpecModel.MAP));
         test0("El tiempo en España", this::checkError);
     }
 }
diff --git a/src/test/java/org/apache/nlpcraft/client/NCClearConversationTest.java b/src/test/java/org/apache/nlpcraft/client/NCClearConversationTest.java
index 7dc0e49..b29e6ae 100644
--- a/src/test/java/org/apache/nlpcraft/client/NCClearConversationTest.java
+++ b/src/test/java/org/apache/nlpcraft/client/NCClearConversationTest.java
@@ -46,10 +46,10 @@
         // missed 'test1'
         check("test2", this::checkError);
 
-        check("test1 test2", this::checkOk);
+        check("test1 test2", res -> checkOk(res, null));
 
         // 'test1' received from conversation.
-        check("test2", this::checkOk);
+        check("test2", res -> checkOk(res, null));
     }
 
     /**
diff --git a/src/test/java/org/apache/nlpcraft/client/NCClearDialogTest.java b/src/test/java/org/apache/nlpcraft/client/NCClearDialogTest.java
index ec8d55f..848d4ca 100644
--- a/src/test/java/org/apache/nlpcraft/client/NCClearDialogTest.java
+++ b/src/test/java/org/apache/nlpcraft/client/NCClearDialogTest.java
@@ -51,14 +51,14 @@
         check("test2", this::checkError);
 
         // `test1` is always ok.
-        check("test1", this::checkOk);
+        check("test1", res -> checkOk(res, null));
 
         // There is one `test1` before.
-        check("test2", this::checkOk);
+        check("test2", res -> checkOk(res, null));
 
         // `test1` is always ok.
-        check("test1", this::checkOk);
-        check("test1", this::checkOk);
+        check("test1", res -> checkOk(res, null));
+        check("test1", res -> checkOk(res, null));
 
         // There are too much `test1` before.
         check("test2", this::checkError);
diff --git a/src/test/java/org/apache/nlpcraft/client/NCCompanyTest.java b/src/test/java/org/apache/nlpcraft/client/NCCompanyTest.java
index 0225602..2cb91ec 100644
--- a/src/test/java/org/apache/nlpcraft/client/NCCompanyTest.java
+++ b/src/test/java/org/apache/nlpcraft/client/NCCompanyTest.java
@@ -50,7 +50,7 @@
         String city,
         String address,
         String postalCode,
-        Map<String, String> props
+        Map<String, Object> props
     ) {
         assertEquals(comp.getName(), name);
         assertEquals(comp.getWebsite(), website);
@@ -69,7 +69,7 @@
      * @param other
      * @throws Exception
      */
-    private void test0(String name, Map<String, String> props, String other) throws Exception {
+    private void test0(String name, Map<String, Object> props, String other) throws Exception {
         String adminEmail = "test@" + UUID.randomUUID() + ".com";
         String adminPwd = "test";
     
@@ -103,6 +103,8 @@
             other = "new " + other;
 
             if (props != null) {
+                props = new HashMap<>(props);
+
                 props.put("new", "new");
             }
     
@@ -137,11 +139,7 @@
      */
     @Test
     void test() throws Exception {
-        Map<String, String> props = new HashMap<>();
-
-        props.put("k1", "v1");
-
-        test0("company", props, "company");
+        test0("company", MAP, "company");
         test0("company", null, null);
     }
 }
diff --git a/src/test/java/org/apache/nlpcraft/client/NCModelTest1.java b/src/test/java/org/apache/nlpcraft/client/NCModelTest1.java
new file mode 100644
index 0000000..89d0994
--- /dev/null
+++ b/src/test/java/org/apache/nlpcraft/client/NCModelTest1.java
@@ -0,0 +1,155 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.nlpcraft.client;
+
+import org.apache.nlpcraft.client.models.NCRestSpecModel;
+import org.apache.nlpcraft.model.NCModel;
+import org.junit.jupiter.api.Test;
+
+import java.util.Comparator;
+import java.util.List;
+import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import static org.apache.nlpcraft.client.models.NCRestSpecModel.MDL_ID;
+import static org.apache.nlpcraft.client.models.NCRestSpecModel.VAL_ELEM_ID1;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * REST client test. Methods `model/syns`,`model/info`.
+ */
+class NCModelTest1 extends NCTestAdapter {
+    @Override
+    Optional<Class<? extends NCModel>> getModelClass() {
+        return Optional.of(NCRestSpecModel.class);
+    }
+
+    @Test
+    void testElementSynonyms() throws Exception {
+        NCElementSynonymsData data = admCli.getSynonyms(MDL_ID, VAL_ELEM_ID1);
+
+        assertTrue(data.getSynonymsCnt() > 0);
+        assertTrue(data.getSynonymsExpCnt() > 0);
+        assertTrue(data.getSynonymsExpRatePct() > 0);
+
+        assertTrue(data.getSynonyms() != null && !data.getSynonyms().isEmpty());
+        assertTrue(data.getSynonymsExp() != null && !data.getSynonymsExp().isEmpty());
+        assertTrue(data.getValues() != null && !data.getValues().isEmpty());
+        assertTrue(data.getValuesExp() != null && !data.getValuesExp().isEmpty());
+    }
+
+    /**
+     * @throws Exception
+     */
+    @Test
+    void testModelInfo() throws Exception {
+        NCRestSpecModel probeMdl = new NCRestSpecModel();
+
+        // Checked model values.
+        assert probeMdl.getElements().stream().anyMatch(p -> p.getValues() != null && !p.getValues().isEmpty());
+
+        NCModelInfo restMdl = admCli.getModelInfo(MDL_ID);
+
+        assertEquals(probeMdl.getId(), restMdl.getId());
+        assertEquals(probeMdl.getName(), restMdl.getName());
+        assertEquals(probeMdl.getDescription(), restMdl.getDescription());
+        assertEquals(probeMdl.getVersion(), restMdl.getVersion());
+        assertEquals(probeMdl.getOrigin(), restMdl.getOrigin());
+        assertEquals(probeMdl.getMaxUnknownWords(), restMdl.getMaxUnknownWords());
+        assertEquals(probeMdl.getMaxFreeWords(), restMdl.getMaxFreeWords());
+        assertEquals(probeMdl.getMaxSuspiciousWords(), restMdl.getMaxSuspiciousWords());
+        assertEquals(probeMdl.getMinWords(), restMdl.getMinWords());
+        assertEquals(probeMdl.getMaxWords(), restMdl.getMaxWords());
+        assertEquals(probeMdl.getMinTokens(), restMdl.getMinTokens());
+        assertEquals(probeMdl.getMaxTokens(), restMdl.getMaxTokens());
+        assertEquals(probeMdl.getMinNonStopwords(), restMdl.getMinNonStopwords());
+        assertEquals(probeMdl.isNonEnglishAllowed(), restMdl.isNonEnglishAllowed());
+        assertEquals(probeMdl.isNotLatinCharsetAllowed(), restMdl.isNotLatinCharsetAllowed());
+        assertEquals(probeMdl.isSwearWordsAllowed(), restMdl.isSwearWordsAllowed());
+        assertEquals(probeMdl.isNoNounsAllowed(), restMdl.isNoNounsAllowed());
+        assertEquals(probeMdl.isPermutateSynonyms(), restMdl.isPermutateSynonyms());
+        assertEquals(probeMdl.isDupSynonymsAllowed(), restMdl.isDupSynonymsAllowed());
+        assertEquals(probeMdl.getMaxTotalSynonyms(), restMdl.getMaxTotalSynonyms());
+        assertEquals(probeMdl.isNoUserTokensAllowed(), restMdl.isNoUserTokensAllowed());
+        assertEquals(probeMdl.isSparse(), restMdl.isSparse());
+        assertEquals(probeMdl.getMetadata(), restMdl.getMetadata());
+        assertEquals(probeMdl.getAdditionalStopWords(), restMdl.getAdditionalStopWords());
+        assertEquals(probeMdl.getExcludedStopWords(), restMdl.getExcludedStopWords());
+        assertEquals(probeMdl.getSuspiciousWords(), restMdl.getSuspiciousWords());
+        assertEquals(probeMdl.getMacros(), restMdl.getMacros());
+        assertEquals(probeMdl.getEnabledBuiltInTokens(), restMdl.getEnabledBuiltInTokens());
+        assertEquals(probeMdl.getAbstractTokens(), restMdl.getAbstractTokens());
+        assertEquals(probeMdl.getMaxElementSynonyms(), restMdl.getMaxElementSynonyms());
+        assertEquals(probeMdl.isMaxSynonymsThresholdError(), restMdl.isMaxSynonymsThresholdError());
+        assertEquals(probeMdl.getConversationTimeout(), restMdl.getConversationTimeout());
+        assertEquals(probeMdl.getConversationDepth(), restMdl.getConversationDepth());
+        assertEquals(probeMdl.getRestrictedCombinations(), restMdl.getRestrictedCombinations());
+
+        Set<org.apache.nlpcraft.model.NCElement> elems1 = probeMdl.getElements();
+        Set<org.apache.nlpcraft.client.NCElement> elems2 = restMdl.getElements();
+
+        assertEquals(elems1.size(), elems2.size());
+
+        int elemsCnt = elems1.size();
+
+        List<org.apache.nlpcraft.model.NCElement> elemList1 =
+            elems1.stream().sorted(Comparator.comparing(org.apache.nlpcraft.model.NCElement::getId)).collect(Collectors.toList());
+
+        List<org.apache.nlpcraft.client.NCElement> elemList2 =
+            elems2.stream().sorted(Comparator.comparing(org.apache.nlpcraft.client.NCElement::getId)).collect(Collectors.toList());
+
+        for (int i = 0; i < elemsCnt; i++) {
+            org.apache.nlpcraft.model.NCElement elemMdl = elemList1.get(i);
+            org.apache.nlpcraft.client.NCElement elemCli = elemList2.get(i);
+
+            assertEquals(elemMdl.getId(), elemCli.getId());
+            assertEquals(elemMdl.getGroups(), elemCli.getGroups());
+            assertEquals(elemMdl.getMetadata(), elemCli.getMetadata());
+            assertEquals(elemMdl.getDescription(), elemCli.getDescription());
+            assertEquals(elemMdl.getParentId(), elemCli.getParentId());
+            assertEquals(elemMdl.getSynonyms(), elemCli.getSynonyms());
+            // TODO: comment because main nlpcraft release 0.9.0 contains errors. Uncomment after fix in new release.
+            //assertEquals(elemMdl.isPermutateSynonyms(), elemCli.isPermutateSynonyms());
+            // TODO: comment because main nlpcraft release 0.9.0 contains errors. Uncomment after fix in new release.
+            //assertEquals(elemMdl.isSparse(), elemCli.isSparse());
+
+            List<org.apache.nlpcraft.model.NCValue> vals1 = elemMdl.getValues();
+            List<org.apache.nlpcraft.client.NCValue> vals2 = elemCli.getValues();
+
+            int valsCnt1 = vals1 == null ? 0 : vals1.size();
+            int valsCnt2 = vals2 == null ? 0 : vals2.size();
+
+            assertEquals(valsCnt1, valsCnt2);
+
+            for (int j = 0; j < valsCnt1; j++) {
+                assert vals2 != null;
+
+                org.apache.nlpcraft.model.NCValue valMdl = vals1.get(i);
+                org.apache.nlpcraft.client.NCValue valCli = vals2.get(i);
+
+                System.out.println("Checked value: " + valMdl.getName());
+
+                assertEquals(valMdl.getName(), valCli.getName());
+                // TODO: comment because main nlpcraft release 0.9.0 contains errors. Uncomment after fix in new release.
+                //assertEquals(valMdl.getSynonyms(), valCli.getSynonyms());
+            }
+        }
+    }
+}
diff --git a/src/test/java/org/apache/nlpcraft/client/NCModelTest.java b/src/test/java/org/apache/nlpcraft/client/NCModelTest2.java
similarity index 75%
rename from src/test/java/org/apache/nlpcraft/client/NCModelTest.java
rename to src/test/java/org/apache/nlpcraft/client/NCModelTest2.java
index de94337..b02d2d7 100644
--- a/src/test/java/org/apache/nlpcraft/client/NCModelTest.java
+++ b/src/test/java/org/apache/nlpcraft/client/NCModelTest2.java
@@ -17,8 +17,7 @@
 
 package org.apache.nlpcraft.client;
 
-import org.apache.nlpcraft.client.models.NCSuggestionSpecModel;
-import org.apache.nlpcraft.model.NCModel;
+import org.apache.nlpcraft.client.models.NCRestSpecModel;
 import org.junit.jupiter.api.Test;
 
 import java.util.Collection;
@@ -26,24 +25,19 @@
 import java.util.Optional;
 import java.util.stream.Collectors;
 
-import static org.apache.nlpcraft.client.models.NCSuggestionSpecModel.MDL_ID;
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertFalse;
-import static org.junit.jupiter.api.Assertions.assertNotNull;
-import static org.junit.jupiter.api.Assertions.assertNull;
-import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.apache.nlpcraft.client.models.NCRestSpecModel.MDL_ID;
+import static org.junit.jupiter.api.Assertions.*;
 
 /**
- * REST client test. Methods `model/sugsyn`.
+ * REST client test. Methods `model/sugsyn`. Required started 'ctxword' server.
  */
-class NCModelTest extends NCTestAdapter {
+class NCModelTest2 extends NCTestAdapter {
     @Override
-    Optional<Class<? extends NCModel>> getModelClass() {
-        return Optional.of(NCSuggestionSpecModel.class);
+    Optional<Class<? extends org.apache.nlpcraft.model.NCModel>> getModelClass() {
+        return Optional.of(NCRestSpecModel.class);
     }
 
     /**
-     *
      * @param s
      * @param minScore
      * @throws Exception
diff --git a/src/test/java/org/apache/nlpcraft/client/NCTestAdapter.java b/src/test/java/org/apache/nlpcraft/client/NCTestAdapter.java
index b09cc71..933b4dc 100644
--- a/src/test/java/org/apache/nlpcraft/client/NCTestAdapter.java
+++ b/src/test/java/org/apache/nlpcraft/client/NCTestAdapter.java
@@ -24,14 +24,13 @@
 
 import java.io.IOException;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.Optional;
 import java.util.function.Predicate;
 
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertNotNull;
-import static org.junit.jupiter.api.Assertions.assertNull;
-import static org.junit.jupiter.api.Assertions.fail;
+import static org.junit.jupiter.api.Assertions.*;
 
 /**
  * REST client test adapter.
@@ -47,14 +46,11 @@
          */
         void run() throws Exception;
     }
-    
     /** */
     protected NCClient admCli;
-    
     /** */
     protected long admUsrId;
 
-    /** */
     private Class<? extends NCModel> mdlClass;
 
     /**
@@ -64,6 +60,20 @@
     Optional<Class<? extends NCModel>> getModelClass() {
         return Optional.empty();
     }
+
+    protected static Map<String, Object> MAP = new HashMap<>();
+
+    static {
+        MAP.put("k1", "v1");
+        MAP.put("k2", 2.2);
+
+        Map<String, Object> m = new HashMap<>();
+
+        m.put("k11", "v11");
+        m.put("k12", 2.22);
+
+        MAP.put("k3", m);
+    }
     
     /**
      *
@@ -172,28 +182,29 @@
     
         return opt.get();
     }
-    
+
     /**
      *
      * @param state
      */
-    protected void checkOk(NCResult state) {
+    protected void checkOk(NCResult state, Map<String, Object> meta) {
         System.out.printf(
             "Text: %s \ntype: %s\nresult: %s%n",
             state.getText(),
             state.getResultType(),
             state.getResultBody()
         );
-    
+
         if (state.getLogHolder() != null)
             System.out.printf("log:\n%s%n", state.getLogHolder());
-        
+
         assertNotNull(state.getResultBody(), "Error: " + state.getErrorMessage());
         assertNotNull(state.getResultType());
         assertNull(state.getErrorMessage());
         assertNull(state.getErrorCode());
+        assertEquals(meta, state.getResultMeta());
     }
-    
+
     /**
      *
      * @param state
diff --git a/src/test/java/org/apache/nlpcraft/client/NCUserTest.java b/src/test/java/org/apache/nlpcraft/client/NCUserTest.java
index 2fa8981..fb5b9c8 100644
--- a/src/test/java/org/apache/nlpcraft/client/NCUserTest.java
+++ b/src/test/java/org/apache/nlpcraft/client/NCUserTest.java
@@ -21,7 +21,6 @@
 import org.apache.nlpcraft.model.NCModel;
 import org.junit.jupiter.api.Test;
 
-import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Optional;
@@ -58,7 +57,7 @@
         String avatar,
         boolean isAdmin,
         String extId,
-        Map<String, String> props
+        Map<String, Object> props
     ) {
         assertEquals(u.getEmail(), email);
         assertEquals(u.getFirstName(), firstName);
@@ -99,13 +98,13 @@
         
         admCli.resetUserPassword(usrId1, "pswd2");
         admCli.updateUserAdmin(usrId1, false);
-        admCli.updateUser(usrId1, "first2", "last2", "av2", null);
+        admCli.updateUser(usrId1, "first2", "last2", "av2", MAP);
     
         users = admCli.getAllUsers();
     
         user = get(users, (u) -> usrId1 == u.getId());
         
-        check(user, "email1@test.com", "first2", "last2", "av2", false, null, null);
+        check(user, "email1@test.com", "first2", "last2", "av2", false, null, MAP);
     
         admCli.deleteUser(usrId1, null);
     
@@ -113,20 +112,16 @@
     
         assertFalse(getOpt(users, (u) -> usrId1 == u.getId()).isPresent());
 
-        Map<String, String> props = new HashMap<>();
-
-        props.put("k1", "v1");
-    
         long usrId2 =
             admCli.addUser(
-                "email1@test.com", "pswd1", "first1", "last1", "av1", false, props, null
+                "email1@test.com", "pswd1", "first1", "last1", "av1", false, MAP, null
             );
     
         users = admCli.getAllUsers();
     
         user = get(users, (u) -> usrId2 == u.getId());
     
-        check(user, "email1@test.com", "first1", "last1", "av1", false, null, props);
+        check(user, "email1@test.com", "first1", "last1", "av1", false, null, MAP);
     
         admCli.deleteUser(usrId2, null);
     
diff --git a/src/test/java/org/apache/nlpcraft/client/models/NCCommonSpecModel.java b/src/test/java/org/apache/nlpcraft/client/models/NCCommonSpecModel.java
index aaac1b3..4849339 100644
--- a/src/test/java/org/apache/nlpcraft/client/models/NCCommonSpecModel.java
+++ b/src/test/java/org/apache/nlpcraft/client/models/NCCommonSpecModel.java
@@ -19,14 +19,32 @@
 
 import org.apache.nlpcraft.model.NCElement;
 import org.apache.nlpcraft.model.NCIntent;
+import org.apache.nlpcraft.model.NCResult;
 
-import java.util.Collections;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
 import java.util.Set;
 
 /**
  * Test model.
  */
 public class NCCommonSpecModel extends NCSpecModelAdapter {
+    public static final Map<String, Object> MAP = new HashMap<>();
+
+    static {
+        MAP.put("k1", "v1");
+        MAP.put("k2", 2.2);
+
+        Map<String, Object> m = new HashMap<>();
+
+        m.put("k11", "v11");
+        m.put("k12", 2.22);
+
+        MAP.put("k3", m);
+    }
+
     public static final String MDL_ID = NCCommonSpecModel.class.getSimpleName();
 
     @Override
@@ -36,11 +54,21 @@
 
     @Override
     public Set<NCElement> getElements() {
-        return Collections.singleton(mkElement("test"));
+        return new HashSet<>(Arrays.asList(mkElement("test"), mkElement("meta")));
     }
 
     @NCIntent("intent=intentId term~{tok_id() == 'test'}")
     public org.apache.nlpcraft.model.NCResult onTest() {
         return org.apache.nlpcraft.model.NCResult.text("OK");
     }
+
+    @NCIntent("intent=intentMetaId term~{tok_id() == 'meta'}")
+    public org.apache.nlpcraft.model.NCResult onMeta() {
+        NCResult res = NCResult.text("OK");
+
+        res.getMetadata().putAll(MAP);
+
+        return res;
+    }
+
 }
diff --git a/src/test/java/org/apache/nlpcraft/client/models/NCRestSpecModel.java b/src/test/java/org/apache/nlpcraft/client/models/NCRestSpecModel.java
new file mode 100644
index 0000000..9ea41a9
--- /dev/null
+++ b/src/test/java/org/apache/nlpcraft/client/models/NCRestSpecModel.java
@@ -0,0 +1,329 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.nlpcraft.client.models;
+
+import org.apache.nlpcraft.model.NCElement;
+import org.apache.nlpcraft.model.NCIntent;
+import org.apache.nlpcraft.model.NCIntentSample;
+import org.apache.nlpcraft.model.NCValue;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+
+/**
+ * Test model for requests: `model/sugsyn`, `model/syns`,`model/info`.
+ */
+public class NCRestSpecModel extends NCSpecModelAdapter {
+    public static final String MDL_ID = NCRestSpecModel.class.getSimpleName();
+    public static final String VAL_ELEM_ID1 = "valTest1";
+    public static final String VAL_ELEM_ID2 = "valTest2";
+
+    private static Set<String> mkSet(String ... data) {
+        return new HashSet<>(Arrays.asList(data));
+    }
+
+    @Override
+    public String getDescription() {
+        return "some desc";
+    }
+
+    @Override
+    public String getOrigin() {
+        return "some origin";
+    }
+
+    @Override
+    public int getMaxUnknownWords() {
+        return 101;
+    }
+
+    @Override
+    public int getMaxFreeWords() {
+        return 22;
+    }
+
+    @Override
+    public int getMaxSuspiciousWords() {
+        return 23;
+    }
+
+    @Override
+    public int getMinWords() {
+        return 2;
+    }
+
+    @Override
+    public int getMaxWords() {
+        return 100;
+    }
+
+    @Override
+    public int getMinTokens() {
+        return 2;
+    }
+
+    @Override
+    public int getMaxTokens() {
+        return 12;
+    }
+
+    @Override
+    public int getMinNonStopwords() {
+        return 3;
+    }
+
+    @Override
+    public boolean isNonEnglishAllowed() {
+        return true;
+    }
+
+    @Override
+    public boolean isNotLatinCharsetAllowed() {
+        return true;
+    }
+
+    @Override
+    public boolean isSwearWordsAllowed() {
+        return true;
+    }
+
+    @Override
+    public boolean isNoNounsAllowed() {
+        return true;
+    }
+
+    @Override
+    public boolean isPermutateSynonyms() {
+        return true;
+    }
+
+    @Override
+    public boolean isDupSynonymsAllowed() {
+        return true;
+    }
+
+    @Override
+    public int getMaxTotalSynonyms() {
+        return 10101;
+    }
+
+    @Override
+    public boolean isNoUserTokensAllowed() {
+        return true;
+    }
+
+    @Override
+    public boolean isSparse() {
+        return true;
+    }
+
+    @Override
+    public Map<String, Object> getMetadata() {
+        Map<String, Object> m = new HashMap<>();
+
+        m.put("k1", 12.12);
+        m.put("k2", "word");
+
+        return m;
+    }
+
+    @Override
+    public Set<String> getAdditionalStopWords() {
+        return mkSet("a", "b");
+    }
+
+    @Override
+    public Set<String> getExcludedStopWords() {
+        return mkSet("x", "y");
+    }
+
+    @Override
+    public Set<String> getSuspiciousWords() {
+        return mkSet("f", "h", "h");
+    }
+
+    @Override
+    public Map<String, String> getMacros() {
+        Map<String, String> m = new HashMap<>();
+
+        m.put("<A>", "{a|_}");
+
+        return m;
+    }
+
+    @Override
+    public Set<String> getEnabledBuiltInTokens() {
+        return mkSet("nlpcraft:limit", "nlpcraft:num");
+    }
+
+    @Override
+    public Set<String> getAbstractTokens() {
+        return mkSet(VAL_ELEM_ID1);
+    }
+
+    @Override
+    public int getMaxElementSynonyms() {
+        return 123;
+    }
+
+    @Override
+    public boolean isMaxSynonymsThresholdError() {
+        return true;
+    }
+
+    @Override
+    public long getConversationTimeout() {
+        return 12345;
+    }
+
+    @Override
+    public int getConversationDepth() {
+        return 3;
+    }
+
+    @Override
+    public Map<String, Set<String>> getRestrictedCombinations() {
+        Map<String, Set<String>> m = new HashMap<>();
+
+        m.put("nlpcraft:limit", mkSet("valTest"));
+
+        return m;
+    }
+
+    @Override
+    public String getId() {
+        return MDL_ID;
+    }
+
+    @Override
+    public Set<NCElement> getElements() {
+        Set<NCElement> set = new HashSet<>();
+
+        set.add(mkElement( "test"));
+
+        Map<String, List<String>> vals = new HashMap<>();
+
+        vals.put("v1", List.of("v11", "v12"));
+        vals.put("v2", List.of("v21", "v22"));
+
+        set.add(
+            new NCElement() {
+                @Override
+                public String getId() {
+                    return VAL_ELEM_ID1;
+                }
+
+                @Override
+                public Map<String, Object> getMetadata() {
+                    Map<String, Object> m = new HashMap<>();
+
+                    m.put("k1", 12.12);
+                    m.put("k2", "word");
+
+                    return m;
+                }
+
+                @Override
+                public String getDescription() {
+                    return "Some description";
+                }
+
+                @Override
+                public List<NCValue> getValues() {
+                    return List.of(
+                        new NCValue() {
+                            @Override
+                            public String getName() {
+                                return "name1";
+                            }
+
+                            @Override
+                            public List<String> getSynonyms() {
+                                return List.of("v11", "v12");
+                            }
+                        },
+                        new NCValue() {
+                            @Override
+                            public String getName() {
+                                return "name2";
+                            }
+
+                            @Override
+                            public List<String> getSynonyms() {
+                                return List.of("v21", "v22");
+                            }
+                        }
+                    );
+                }
+
+                @Override
+                public List<String> getSynonyms() {
+                    return List.of("vt1", "vt2");
+                }
+
+                @Override
+                public Optional<Boolean> isPermutateSynonyms() {
+                    return Optional.empty();
+                }
+
+                @Override
+                public Optional<Boolean> isSparse() {
+                    return Optional.of(true);
+                }
+            }
+        );
+
+        set.add(
+            new NCElement() {
+                @Override
+                public String getId() {
+                    return VAL_ELEM_ID2;
+                }
+
+                @Override
+                public Optional<Boolean> isPermutateSynonyms() {
+                    return Optional.of(true);
+                }
+
+                @Override
+                public Optional<Boolean> isSparse() {
+                    return Optional.of(false);
+                }
+
+
+                @Override
+                public String getParentId() {
+                    return "valTest1";
+                }
+            }
+        );
+
+        return set;
+    }
+
+    @NCIntentSample({"Tests passed well"})
+    @NCIntent("intent=intentId term~{tok_id() == 'test'}")
+    public org.apache.nlpcraft.model.NCResult onTest() {
+        return org.apache.nlpcraft.model.NCResult.text("OK");
+    }
+}
diff --git a/src/test/java/org/apache/nlpcraft/client/models/NCSpecModelAdapter.java b/src/test/java/org/apache/nlpcraft/client/models/NCSpecModelAdapter.java
index 8816a9b..19c7c42 100644
--- a/src/test/java/org/apache/nlpcraft/client/models/NCSpecModelAdapter.java
+++ b/src/test/java/org/apache/nlpcraft/client/models/NCSpecModelAdapter.java
@@ -38,6 +38,12 @@
         return "1.0.0";
     }
 
+    /**
+     *
+     * @param id
+     * @param syns
+     * @return
+     */
     protected NCElement mkElement(String id, String... syns) {
         return new NCElement() {
             @Override
@@ -49,8 +55,11 @@
             public List<String> getSynonyms() {
                 List<String> l = new ArrayList<>();
 
+                if (syns.length != 0) {
+                    l.addAll(Arrays.asList(syns));
+                }
+
                 l.add(id);
-                l.addAll(Arrays.asList(syns));
 
                 return l;
             }
diff --git a/src/test/java/org/apache/nlpcraft/client/models/NCSuggestionSpecModel.java b/src/test/java/org/apache/nlpcraft/client/models/NCSuggestionSpecModel.java
deleted file mode 100644
index c757ca1..0000000
--- a/src/test/java/org/apache/nlpcraft/client/models/NCSuggestionSpecModel.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.nlpcraft.client.models;
-
-import org.apache.nlpcraft.model.NCElement;
-import org.apache.nlpcraft.model.NCIntent;
-import org.apache.nlpcraft.model.NCIntentSample;
-
-import java.util.Collections;
-import java.util.Set;
-
-/**
- * Test model.
- */
-public class NCSuggestionSpecModel extends NCSpecModelAdapter {
-    public static final String MDL_ID = NCSuggestionSpecModel.class.getSimpleName();
-
-    @Override
-    public String getId() {
-        return MDL_ID;
-    }
-
-    @Override
-    public Set<NCElement> getElements() {
-        return Collections.singleton(mkElement( "test"));
-    }
-
-    @NCIntentSample({"Tests passed well"})
-    @NCIntent("intent=intentId term~{tok_id() == 'test'}")
-    public org.apache.nlpcraft.model.NCResult onTest() {
-        return org.apache.nlpcraft.model.NCResult.text("OK");
-    }
-}