You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@unomi.apache.org by sh...@apache.org on 2022/12/13 14:13:46 UTC

svn commit: r1905962 [2/4] - in /unomi/website/manual: 2_1_x/ 2_1_x/images/ 2_1_x/jsonSchema/ 2_1_x/migrations/ latest/ latest/connectors/ latest/images/ latest/jsonSchema/ latest/migrations/ latest/samples/

Modified: unomi/website/manual/2_1_x/index.html
URL: http://svn.apache.org/viewvc/unomi/website/manual/2_1_x/index.html?rev=1905962&r1=1905961&r2=1905962&view=diff
==============================================================================
--- unomi/website/manual/2_1_x/index.html (original)
+++ unomi/website/manual/2_1_x/index.html Tue Dec 13 14:13:46 2022
@@ -6,385 +6,492 @@
 <meta name="viewport" content="width=device-width, initial-scale=1.0">
 <meta name="generator" content="Asciidoctor 1.5.8">
 <meta name="author" content="Apache Software Foundation">
-<title>Apache Unomi 1.x - Documentation</title>
+<title>Apache Unomi 2.x - Documentation</title>
 <link rel="stylesheet" href="./apache.css">
 </head>
 <body class="article toc2 toc-left">
 <div id="header">
-<h1>Apache Unomi 1.x - Documentation</h1>
+<h1>Apache Unomi 2.x - Documentation</h1>
 <div class="details">
 <span id="author" class="author">Apache Software Foundation</span><br>
 </div>
 <div id="toc" class="toc2">
 <div id="toctitle">Table of Contents</div>
 <ul class="sectlevel1">
-<li><a href="#_quick_start">1. Quick start</a>
+<li><a href="#_whats_new">1. What&#8217;s new</a>
 <ul class="sectlevel2">
-<li><a href="#_five_minutes_quickstart">1.1. Five Minutes QuickStart</a></li>
+<li><a href="#_whats_new_in_apache_unomi_2_0">1.1. What&#8217;s new in Apache Unomi 2.0</a>
+<ul class="sectlevel3">
+<li><a href="#_introducing_profiles_aliases">1.1.1. Introducing profiles aliases</a></li>
+<li><a href="#_scopes_declarations_are_now_required">1.1.2. Scopes declarations are now required</a></li>
+<li><a href="#_json_schemas">1.1.3. JSON Schemas</a></li>
+<li><a href="#_updated_data_model">1.1.4. Updated data model</a></li>
+<li><a href="#_new_web_tracker">1.1.5. New Web Tracker</a></li>
+<li><a href="#_graphql_api_beta">1.1.6. GraphQL API - beta</a></li>
+<li><a href="#_migrate_from_unomi_1_x">1.1.7. Migrate from Unomi 1.x</a></li>
+<li><a href="#_elasticsearch_compatibility">1.1.8. Elasticsearch compatibility</a></li>
+</ul>
+</li>
 </ul>
 </li>
-<li><a href="#_first_steps_with_apache_unomi">2. First steps with Apache Unomi</a>
+<li><a href="#_discover_unomi">2. Discover Unomi</a>
 <ul class="sectlevel2">
-<li><a href="#_getting_started_with_unomi">2.1. Getting started with Unomi</a>
+<li><a href="#_quick_start_with_docker">2.1. Quick start with Docker</a></li>
+<li><a href="#_quick_start_manually">2.2. Quick Start manually</a></li>
+<li><a href="#_getting_started_with_unomi">2.3. Getting started with Unomi</a>
 <ul class="sectlevel3">
-<li><a href="#_prerequisites">2.1.1. Prerequisites</a></li>
-<li><a href="#_running_unomi">2.1.2. Running Unomi</a></li>
+<li><a href="#_prerequisites">2.3.1. Prerequisites</a></li>
+<li><a href="#_running_unomi">2.3.2. Running Unomi</a></li>
 </ul>
 </li>
-<li><a href="#_recipes">2.2. Recipes</a>
+<li><a href="#_unomi_web_tracking_tutorial">2.4. Unomi web tracking tutorial</a>
 <ul class="sectlevel3">
-<li><a href="#_introduction">2.2.1. Introduction</a></li>
-<li><a href="#_how_to_read_a_profile">2.2.2. How to read a profile</a></li>
-<li><a href="#_how_to_update_a_profile_from_the_public_internet">2.2.3. How to update a profile from the public internet</a></li>
-<li><a href="#_how_to_search_for_profile_events">2.2.4. How to search for profile events</a></li>
-<li><a href="#_how_to_create_a_new_rule">2.2.5. How to create a new rule</a></li>
-<li><a href="#_how_to_search_for_profiles">2.2.6. How to search for profiles</a></li>
-<li><a href="#_getting_updating_consents">2.2.7. Getting / updating consents</a></li>
-<li><a href="#_how_to_send_a_login_event_to_unomi">2.2.8. How to send a login event to Unomi</a></li>
+<li><a href="#_installing_the_web_tracker_in_a_web_page">2.4.1. Installing the web tracker in a web page</a></li>
+<li><a href="#_creating_a_scope_to_collect_the_data">2.4.2. Creating a scope to collect the data</a></li>
+<li><a href="#_using_tracker_in_your_own_javascript_projects">2.4.3. Using tracker in your own JavaScript projects</a></li>
+<li><a href="#_viewing_collected_events">2.4.4. Viewing collected events</a></li>
+<li><a href="#_viewing_the_current_profile">2.4.5. Viewing the current profile</a></li>
+<li><a href="#_adding_a_rule">2.4.6. Adding a rule</a></li>
+<li><a href="#_adding_personalization">2.4.7. Adding personalization</a></li>
+<li><a href="#_conclusion">2.4.8. Conclusion</a></li>
+<li><a href="#_next_steps">2.4.9. Next steps</a></li>
+</ul>
+</li>
 </ul>
 </li>
-<li><a href="#_request_examples">2.3. Request examples</a>
+<li><a href="#_apache_unomi_recipes_and_requests">3. Apache Unomi Recipes and requests</a>
+<ul class="sectlevel2">
+<li><a href="#_recipes">3.1. Recipes</a>
 <ul class="sectlevel3">
-<li><a href="#_retrieving_your_first_context">2.3.1. Retrieving your first context</a></li>
-<li><a href="#_retrieving_a_context_as_a_json_object">2.3.2. Retrieving a context as a JSON object.</a></li>
-<li><a href="#_accessing_profile_properties_in_a_context">2.3.3. Accessing profile properties in a context</a></li>
-<li><a href="#_sending_events_using_the_context_servlet">2.3.4. Sending events using the context servlet</a></li>
-<li><a href="#_sending_events_using_the_eventcollector_servlet">2.3.5. Sending events using the eventcollector servlet</a></li>
-<li><a href="#_where_to_go_from_here">2.3.6. Where to go from here</a></li>
+<li><a href="#_introduction">3.1.1. Introduction</a></li>
+<li><a href="#_enabling_debug_mode">3.1.2. Enabling debug mode</a></li>
+<li><a href="#_how_to_read_a_profile">3.1.3. How to read a profile</a></li>
+<li><a href="#_how_to_update_a_profile_from_the_public_internet">3.1.4. How to update a profile from the public internet</a></li>
+<li><a href="#_how_to_search_for_profile_events">3.1.5. How to search for profile events</a></li>
+<li><a href="#_how_to_create_a_new_rule">3.1.6. How to create a new rule</a></li>
+<li><a href="#_how_to_search_for_profiles">3.1.7. How to search for profiles</a></li>
+<li><a href="#_getting_updating_consents">3.1.8. Getting / updating consents</a></li>
+<li><a href="#_how_to_send_a_login_event_to_unomi">3.1.9. How to send a login event to Unomi</a></li>
+<li><a href="#_what_profile_aliases_are_and_how_to_use_them">3.1.10. What profile aliases are and how to use them</a></li>
 </ul>
 </li>
-<li><a href="#_web_tracker">2.4. Web Tracker</a>
+<li><a href="#_request_examples">3.2. Request examples</a>
 <ul class="sectlevel3">
-<li><a href="#_getting_started">2.4.1. Getting started</a></li>
-<li><a href="#_how_to_contribute">2.4.2. How to contribute</a></li>
-<li><a href="#_tracking_page_views">2.4.3. Tracking page views</a></li>
-<li><a href="#_tracking_form_submissions">2.4.4. Tracking form submissions</a></li>
+<li><a href="#_retrieving_your_first_context">3.2.1. Retrieving your first context</a></li>
+<li><a href="#_retrieving_a_context_as_a_json_object">3.2.2. Retrieving a context as a JSON object.</a></li>
+<li><a href="#_accessing_profile_properties_in_a_context">3.2.3. Accessing profile properties in a context</a></li>
+<li><a href="#_sending_events_using_the_context_servlet">3.2.4. Sending events using the context servlet</a></li>
+<li><a href="#_sending_events_using_the_eventcollector_servlet">3.2.5. Sending events using the eventcollector servlet</a></li>
+<li><a href="#_where_to_go_from_here">3.2.6. Where to go from here</a></li>
+</ul>
+</li>
 </ul>
 </li>
-<li><a href="#_configuration">2.5. Configuration</a>
+<li><a href="#_configuration">4. Configuration</a>
+<ul class="sectlevel2">
+<li><a href="#_centralized_configuration">4.1. Centralized configuration</a></li>
+<li><a href="#_changing_the_default_configuration_using_environment_variables_i_e_docker_configuration">4.2. Changing the default configuration using environment variables (i.e. Docker configuration)</a></li>
+<li><a href="#_changing_the_default_configuration_using_property_files">4.3. Changing the default configuration using property files</a></li>
+<li><a href="#_secured_events_configuration">4.4. Secured events configuration</a></li>
+<li><a href="#_installing_the_maxmind_geoiplite2_ip_lookup_database">4.5. Installing the MaxMind GeoIPLite2 IP lookup database</a></li>
+<li><a href="#_installing_geonames_database">4.6. Installing Geonames database</a></li>
+<li><a href="#_rest_api_security">4.7. REST API Security</a></li>
+<li><a href="#_scripting_security">4.8. Scripting security</a>
 <ul class="sectlevel3">
-<li><a href="#_centralized_configuration">2.5.1. Centralized configuration</a></li>
-<li><a href="#_changing_the_default_configuration_using_environment_variables_i_e_docker_configuration">2.5.2. Changing the default configuration using environment variables (i.e. Docker configuration)</a></li>
-<li><a href="#_changing_the_default_configuration_using_property_files">2.5.3. Changing the default configuration using property files</a></li>
-<li><a href="#_secured_events_configuration">2.5.4. Secured events configuration</a></li>
-<li><a href="#_installing_the_maxmind_geoiplite2_ip_lookup_database">2.5.5. Installing the MaxMind GeoIPLite2 IP lookup database</a></li>
-<li><a href="#_installing_geonames_database">2.5.6. Installing Geonames database</a></li>
-<li><a href="#_rest_api_security">2.5.7. REST API Security</a></li>
-<li><a href="#_scripting_security">2.5.8. Scripting security</a></li>
-<li><a href="#_groovy_actions">2.5.9. Groovy Actions</a></li>
-<li><a href="#_scripting_roadmap">2.5.10. Scripting roadmap</a></li>
-<li><a href="#_automatic_profile_merging">2.5.11. Automatic profile merging</a></li>
-<li><a href="#_securing_a_production_environment">2.5.12. Securing a production environment</a></li>
-<li><a href="#_integrating_with_an_apache_http_web_server">2.5.13. Integrating with an Apache HTTP web server</a></li>
-<li><a href="#_changing_the_default_tracking_location">2.5.14. Changing the default tracking location</a></li>
-<li><a href="#_apache_karaf_ssh_console">2.5.15. Apache Karaf SSH Console</a></li>
-<li><a href="#_elasticsearch_authentication_and_security">2.5.16. ElasticSearch authentication and security</a></li>
+<li><a href="#_multi_layer_scripting_filtering_system">4.8.1. Multi-layer scripting filtering system</a></li>
+<li><a href="#_scripts_and_expressions">4.8.2. Scripts and expressions</a></li>
+<li><a href="#_scripting_expression_filtering_configuration_parameters">4.8.3. Scripting expression filtering configuration parameters</a></li>
+<li><a href="#_groovy_actions">4.8.4. Groovy Actions</a></li>
+<li><a href="#_scripting_roadmap">4.8.5. Scripting roadmap</a></li>
 </ul>
 </li>
-<li><a href="#_useful_apache_unomi_urls">2.6. Useful Apache Unomi URLs</a></li>
-<li><a href="#_how_profile_tracking_works">2.7. How profile tracking works</a>
+<li><a href="#_automatic_profile_merging">4.9. Automatic profile merging</a></li>
+<li><a href="#_securing_a_production_environment">4.10. Securing a production environment</a></li>
+<li><a href="#_integrating_with_an_apache_http_web_server">4.11. Integrating with an Apache HTTP web server</a></li>
+<li><a href="#_changing_the_default_tracking_location">4.12. Changing the default tracking location</a></li>
+<li><a href="#_apache_karaf_ssh_console">4.13. Apache Karaf SSH Console</a></li>
+<li><a href="#_elasticsearch_authentication_and_security">4.14. ElasticSearch authentication and security</a>
 <ul class="sectlevel3">
-<li><a href="#_steps">2.7.1. Steps</a></li>
+<li><a href="#_user_authentication">4.14.1. User authentication !</a></li>
+<li><a href="#_ssl_communication">4.14.2. SSL communication</a></li>
+<li><a href="#_permissions">4.14.3. Permissions</a></li>
 </ul>
 </li>
-<li><a href="#_context_request_flow">2.8. Context Request Flow</a></li>
 </ul>
 </li>
-<li><a href="#_queries_and_aggregations">3. Queries and aggregations</a>
+<li><a href="#_json_schemas_2">5. JSON schemas</a>
 <ul class="sectlevel2">
-<li><a href="#_query_counts">3.1. Query counts</a></li>
-<li><a href="#_metrics">3.2. Metrics</a></li>
-<li><a href="#_aggregations">3.3. Aggregations</a>
+<li><a href="#_introduction_2">5.1. Introduction</a>
 <ul class="sectlevel3">
-<li><a href="#_aggregation_types">3.3.1. Aggregation types</a></li>
+<li><a href="#_what_is_a_json_schema">5.1.1. What is a JSON Schema</a></li>
+<li><a href="#_key_concepts">5.1.2. Key concepts</a></li>
+<li><a href="#_how_are_json_schema_used_in_unomi">5.1.3. How are JSON Schema used in Unomi</a></li>
 </ul>
 </li>
+<li><a href="#_json_schema_api">5.2. JSON schema API</a>
+<ul class="sectlevel3">
+<li><a href="#_list_existing_schemas">5.2.1. List existing schemas</a></li>
+<li><a href="#_read_a_schema">5.2.2. Read a schema</a></li>
+<li><a href="#_create_update_a_json_schema_to_validate_an_event">5.2.3. Create / update a JSON schema to validate an event</a></li>
+<li><a href="#_deleting_a_schema">5.2.4. Deleting a schema</a></li>
+<li><a href="#_error_management">5.2.5. Error Management</a></li>
+<li><a href="#_details_on_invalid_events">5.2.6. Details on invalid events</a></li>
 </ul>
 </li>
-<li><a href="#_profile_import_export">4. Profile import &amp; export</a>
+<li><a href="#_extend_an_existing_schema">5.3. Extend an existing schema</a>
+<ul class="sectlevel3">
+<li><a href="#_when_a_extension_is_needed">5.3.1. When a extension is needed?</a></li>
+<li><a href="#_understanding_how_extensions_are_merged_in_unomi">5.3.2. Understanding how extensions are merged in unomi</a></li>
+<li><a href="#_how_to_add_an_extension_through_the_api">5.3.3. How to add an extension through the API</a></li>
+</ul>
+</li>
+</ul>
+</li>
+<li><a href="#_graphql_api">6. GraphQL API</a>
 <ul class="sectlevel2">
-<li><a href="#_importing_profiles">4.1. Importing profiles</a>
+<li><a href="#_introduction_3">6.1. Introduction</a></li>
+<li><a href="#_enabling_the_api">6.2. Enabling the API</a></li>
+<li><a href="#_endpoints">6.3. Endpoints</a></li>
+<li><a href="#_graphql_schema">6.4. GraphQL Schema</a></li>
+</ul>
+</li>
+<li><a href="#_migrations">7. Migrations</a>
+<ul class="sectlevel2">
+<li><a href="#_from_version_1_6_to_2_0">7.1. From version 1.6 to 2.0</a></li>
+<li><a href="#_migration_overview">7.2. Migration Overview</a></li>
+<li><a href="#_updating_applications_consuming_unomi">7.3. Updating applications consuming Unomi</a>
 <ul class="sectlevel3">
-<li><a href="#_import_api">4.1.1. Import API</a></li>
+<li><a href="#_data_model_changes">7.3.1. Data Model changes</a></li>
+<li><a href="#_create_json_schemas">7.3.2. Create JSON schemas</a></li>
 </ul>
 </li>
-<li><a href="#_exporting_profiles">4.2. Exporting profiles</a>
+<li><a href="#_migrating_your_existing_data">7.4. Migrating your existing data</a>
 <ul class="sectlevel3">
-<li><a href="#_export_api">4.2.1. Export API</a></li>
+<li><a href="#_elasticsearch_version_and_capacity">7.4.1. Elasticsearch version and capacity</a></li>
+<li><a href="#_migrate_custom_data">7.4.2. Migrate custom data</a></li>
+<li><a href="#_perform_the_migration">7.4.3. Perform the migration</a></li>
 </ul>
 </li>
-<li><a href="#_configuration_in_details">4.3. Configuration in details</a></li>
+<li><a href="#_from_version_1_5_to_1_6">7.5. From version 1.5 to 1.6</a></li>
+<li><a href="#_from_version_1_4_to_1_5">7.6. From version 1.4 to 1.5</a>
+<ul class="sectlevel3">
+<li><a href="#_data_model_and_elasticsearch_7">7.6.1. Data model and ElasticSearch 7</a></li>
+<li><a href="#_api_changes">7.6.2. API changes</a></li>
+<li><a href="#_migration_steps">7.6.3. Migration steps</a></li>
 </ul>
 </li>
-<li><a href="#_consent_management">5. Consent management</a>
+<li><a href="#_important_changes_in_public_servlets_since_version_1_5_5_and_2_0_0">7.7. Important changes in public servlets since version 1.5.5 and 2.0.0</a></li>
+</ul>
+</li>
+<li><a href="#_queries_and_aggregations">8. Queries and aggregations</a>
+<ul class="sectlevel2">
+<li><a href="#_query_counts">8.1. Query counts</a></li>
+<li><a href="#_metrics">8.2. Metrics</a></li>
+<li><a href="#_aggregations">8.3. Aggregations</a>
+<ul class="sectlevel3">
+<li><a href="#_aggregation_types">8.3.1. Aggregation types</a></li>
+</ul>
+</li>
+</ul>
+</li>
+<li><a href="#_profile_import_export">9. Profile import &amp; export</a>
+<ul class="sectlevel2">
+<li><a href="#_importing_profiles">9.1. Importing profiles</a>
+<ul class="sectlevel3">
+<li><a href="#_import_api">9.1.1. Import API</a></li>
+</ul>
+</li>
+<li><a href="#_exporting_profiles">9.2. Exporting profiles</a>
+<ul class="sectlevel3">
+<li><a href="#_export_api">9.2.1. Export API</a></li>
+</ul>
+</li>
+<li><a href="#_configuration_in_details">9.3. Configuration in details</a></li>
+</ul>
+</li>
+<li><a href="#_consent_management">10. Consent management</a>
 <ul class="sectlevel2">
-<li><a href="#_consent_api">5.1. Consent API</a>
+<li><a href="#_consent_api">10.1. Consent API</a>
 <ul class="sectlevel3">
-<li><a href="#_profiles_with_consents">5.1.1. Profiles with consents</a></li>
-<li><a href="#_consent_type_definitions">5.1.2. Consent type definitions</a></li>
-<li><a href="#_creating_update_a_visitor_consent">5.1.3. Creating / update a visitor consent</a></li>
-<li><a href="#_how_it_works_internally">5.1.4. How it works (internally)</a></li>
+<li><a href="#_profiles_with_consents">10.1.1. Profiles with consents</a></li>
+<li><a href="#_consent_type_definitions">10.1.2. Consent type definitions</a></li>
+<li><a href="#_creating_update_a_visitor_consent">10.1.3. Creating / update a visitor consent</a></li>
+<li><a href="#_how_it_works_internally">10.1.4. How it works (internally)</a></li>
 </ul>
 </li>
 </ul>
 </li>
-<li><a href="#_privacy_management">6. Privacy management</a>
+<li><a href="#_privacy_management">11. Privacy management</a>
 <ul class="sectlevel2">
-<li><a href="#_setting_up_access_to_the_privacy_endpoint">6.1. Setting up access to the privacy endpoint</a></li>
-<li><a href="#_anonymizing_a_profile">6.2. Anonymizing a profile</a></li>
-<li><a href="#_downloading_profile_data">6.3. Downloading profile data</a></li>
-<li><a href="#_deleting_a_profile">6.4. Deleting a profile</a></li>
-<li><a href="#_related">6.5. Related</a></li>
+<li><a href="#_setting_up_access_to_the_privacy_endpoint">11.1. Setting up access to the privacy endpoint</a></li>
+<li><a href="#_anonymizing_a_profile">11.2. Anonymizing a profile</a></li>
+<li><a href="#_downloading_profile_data">11.3. Downloading profile data</a></li>
+<li><a href="#_deleting_a_profile">11.4. Deleting a profile</a></li>
+<li><a href="#_related">11.5. Related</a></li>
 </ul>
 </li>
-<li><a href="#_cluster_setup">7. Cluster setup</a>
+<li><a href="#_cluster_setup">12. Cluster setup</a>
 <ul class="sectlevel2">
-<li><a href="#_cluster_setup_2">7.1. Cluster setup</a></li>
+<li><a href="#_cluster_setup_2">12.1. Cluster setup</a></li>
 </ul>
 </li>
-<li><a href="#_reference">8. Reference</a>
+<li><a href="#_reference">13. Reference</a>
 <ul class="sectlevel2">
-<li><a href="#_data_model_overview">8.1. Data Model Overview</a></li>
-<li><a href="#_scope">8.2. Scope</a>
+<li><a href="#_useful_apache_unomi_urls">13.1. Useful Apache Unomi URLs</a></li>
+<li><a href="#_how_profile_tracking_works">13.2. How profile tracking works</a>
 <ul class="sectlevel3">
-<li><a href="#_example">8.2.1. Example</a></li>
+<li><a href="#_steps">13.2.1. Steps</a></li>
 </ul>
 </li>
-<li><a href="#_item">8.3. Item</a>
+<li><a href="#_context_request_flow">13.3. Context Request Flow</a></li>
+<li><a href="#_data_model_overview">13.4. Data Model Overview</a></li>
+<li><a href="#_scope">13.5. Scope</a>
 <ul class="sectlevel3">
-<li><a href="#_structure_definition">8.3.1. Structure definition</a></li>
+<li><a href="#_example_2">13.5.1. Example</a></li>
 </ul>
 </li>
-<li><a href="#_metadata">8.4. Metadata</a>
+<li><a href="#_item">13.6. Item</a>
 <ul class="sectlevel3">
-<li><a href="#_structure_definition_2">8.4.1. Structure definition</a></li>
-<li><a href="#_example_2">8.4.2. Example</a></li>
+<li><a href="#_structure_definition">13.6.1. Structure definition</a></li>
 </ul>
 </li>
-<li><a href="#_metadataitem">8.5. MetadataItem</a>
+<li><a href="#_metadata">13.7. Metadata</a>
 <ul class="sectlevel3">
-<li><a href="#_structure_definition_3">8.5.1. Structure definition</a></li>
-<li><a href="#_example_3">8.5.2. Example</a></li>
+<li><a href="#_structure_definition_2">13.7.1. Structure definition</a></li>
+<li><a href="#_example_3">13.7.2. Example</a></li>
 </ul>
 </li>
-<li><a href="#_event">8.6. Event</a>
+<li><a href="#_metadataitem">13.8. MetadataItem</a>
 <ul class="sectlevel3">
-<li><a href="#_fields">8.6.1. Fields</a></li>
-<li><a href="#_event_types">8.6.2. Event types</a></li>
+<li><a href="#_structure_definition_3">13.8.1. Structure definition</a></li>
+<li><a href="#_example_4">13.8.2. Example</a></li>
 </ul>
 </li>
-<li><a href="#_profile">8.7. Profile</a>
+<li><a href="#_event">13.9. Event</a>
 <ul class="sectlevel3">
-<li><a href="#_structure_definition_4">8.7.1. Structure definition</a></li>
-<li><a href="#_example_4">8.7.2. Example</a></li>
+<li><a href="#_fields">13.9.1. Fields</a></li>
+<li><a href="#_event_types">13.9.2. Event types</a></li>
 </ul>
 </li>
-<li><a href="#_persona">8.8. Persona</a>
+<li><a href="#_profile">13.10. Profile</a>
 <ul class="sectlevel3">
-<li><a href="#_structure_definition_5">8.8.1. Structure definition</a></li>
-<li><a href="#_example_5">8.8.2. Example</a></li>
+<li><a href="#_structure_definition_4">13.10.1. Structure definition</a></li>
+<li><a href="#_example_5">13.10.2. Example</a></li>
 </ul>
 </li>
-<li><a href="#_consent">8.9. Consent</a>
+<li><a href="#_profile_aliases">13.11. Profile aliases</a>
 <ul class="sectlevel3">
-<li><a href="#_structure_definition_6">8.9.1. Structure definition</a></li>
-<li><a href="#_example_6">8.9.2. Example</a></li>
+<li><a href="#_structure_definition_5">13.11.1. Structure definition</a></li>
+<li><a href="#_example_6">13.11.2. Example</a></li>
 </ul>
 </li>
-<li><a href="#_session">8.10. Session</a>
+<li><a href="#_persona">13.12. Persona</a>
 <ul class="sectlevel3">
-<li><a href="#_structure_definition_7">8.10.1. Structure definition</a></li>
-<li><a href="#_example_7">8.10.2. Example</a></li>
+<li><a href="#_structure_definition_6">13.12.1. Structure definition</a></li>
+<li><a href="#_example_7">13.12.2. Example</a></li>
 </ul>
 </li>
-<li><a href="#_segment">8.11. Segment</a>
+<li><a href="#_consent">13.13. Consent</a>
 <ul class="sectlevel3">
-<li><a href="#_structure_definition_8">8.11.1. Structure definition</a></li>
-<li><a href="#_example_8">8.11.2. Example</a></li>
+<li><a href="#_structure_definition_7">13.13.1. Structure definition</a></li>
+<li><a href="#_example_8">13.13.2. Example</a></li>
 </ul>
 </li>
-<li><a href="#_condition">8.12. Condition</a>
+<li><a href="#_session">13.14. Session</a>
 <ul class="sectlevel3">
-<li><a href="#_structure_definition_9">8.12.1. Structure definition</a></li>
-<li><a href="#_example_9">8.12.2. Example</a></li>
+<li><a href="#_structure_definition_8">13.14.1. Structure definition</a></li>
+<li><a href="#_example_9">13.14.2. Example</a></li>
 </ul>
 </li>
-<li><a href="#_rule">8.13. Rule</a>
+<li><a href="#_segment">13.15. Segment</a>
 <ul class="sectlevel3">
-<li><a href="#_structure_definition_10">8.13.1. Structure definition</a></li>
-<li><a href="#_example_10">8.13.2. Example</a></li>
+<li><a href="#_structure_definition_9">13.15.1. Structure definition</a></li>
+<li><a href="#_example_10">13.15.2. Example</a></li>
 </ul>
 </li>
-<li><a href="#_action">8.14. Action</a>
+<li><a href="#_condition">13.16. Condition</a>
 <ul class="sectlevel3">
-<li><a href="#_structure_definition_11">8.14.1. Structure definition</a></li>
-<li><a href="#_example_11">8.14.2. Example</a></li>
+<li><a href="#_structure_definition_10">13.16.1. Structure definition</a></li>
+<li><a href="#_example_11">13.16.2. Example</a></li>
 </ul>
 </li>
-<li><a href="#_list">8.15. List</a>
+<li><a href="#_rule">13.17. Rule</a>
 <ul class="sectlevel3">
-<li><a href="#_structure_definition_12">8.15.1. Structure definition</a></li>
-<li><a href="#_example_12">8.15.2. Example</a></li>
+<li><a href="#_structure_definition_11">13.17.1. Structure definition</a></li>
+<li><a href="#_example_12">13.17.2. Example</a></li>
 </ul>
 </li>
-<li><a href="#_goal">8.16. Goal</a>
+<li><a href="#_action">13.18. Action</a>
 <ul class="sectlevel3">
-<li><a href="#_structure_definition_13">8.16.1. Structure definition</a></li>
-<li><a href="#_example_13">8.16.2. Example</a></li>
+<li><a href="#_structure_definition_12">13.18.1. Structure definition</a></li>
+<li><a href="#_example_13">13.18.2. Example</a></li>
 </ul>
 </li>
-<li><a href="#_campaign">8.17. Campaign</a>
+<li><a href="#_list">13.19. List</a>
 <ul class="sectlevel3">
-<li><a href="#_structure_definition_14">8.17.1. Structure definition</a></li>
-<li><a href="#_example_14">8.17.2. Example</a></li>
+<li><a href="#_structure_definition_13">13.19.1. Structure definition</a></li>
+<li><a href="#_example_14">13.19.2. Example</a></li>
 </ul>
 </li>
-<li><a href="#_scoring_plan">8.18. Scoring plan</a>
+<li><a href="#_goal">13.20. Goal</a>
 <ul class="sectlevel3">
-<li><a href="#_structure_definition_15">8.18.1. Structure definition</a></li>
-<li><a href="#_example_15">8.18.2. Example</a></li>
+<li><a href="#_structure_definition_14">13.20.1. Structure definition</a></li>
+<li><a href="#_example_15">13.20.2. Example</a></li>
 </ul>
 </li>
-<li><a href="#_data_model_changes_for_apache_unomi_1_5_0">8.19. Data Model changes for Apache Unomi 1.5.0</a>
+<li><a href="#_campaign">13.21. Campaign</a>
 <ul class="sectlevel3">
-<li><a href="#_data_model_and_elasticsearch_7">8.19.1. Data model and ElasticSearch 7</a></li>
-<li><a href="#_api_changes">8.19.2. API changes</a></li>
+<li><a href="#_structure_definition_15">13.21.1. Structure definition</a></li>
+<li><a href="#_example_16">13.21.2. Example</a></li>
 </ul>
 </li>
-<li><a href="#_important_changes_in_public_servlets_since_version_1_5_5">8.20. Important changes in public servlets since version 1.5.5</a></li>
-<li><a href="#_built_in_event_types">8.21. Built-in Event types</a>
+<li><a href="#_scoring_plan">13.22. Scoring plan</a>
 <ul class="sectlevel3">
-<li><a href="#_login_event_type">8.21.1. Login event type</a></li>
-<li><a href="#_view_event_type">8.21.2. View event type</a></li>
-<li><a href="#_form_event_type">8.21.3. Form event type</a></li>
-<li><a href="#_update_properties_event_type">8.21.4. Update properties event type</a></li>
-<li><a href="#_identify_event_type">8.21.5. Identify event type</a></li>
-<li><a href="#_session_created_event_type">8.21.6. Session created event type</a></li>
-<li><a href="#_goal_event_type">8.21.7. Goal event type</a></li>
-<li><a href="#_modify_consent_event_type">8.21.8. Modify consent event type</a></li>
+<li><a href="#_structure_definition_16">13.22.1. Structure definition</a></li>
+<li><a href="#_example_17">13.22.2. Example</a></li>
 </ul>
 </li>
-<li><a href="#_built_in_condition_types">8.22. Built-in condition types</a>
+<li><a href="#_built_in_event_types">13.23. Built-in Event types</a>
 <ul class="sectlevel3">
-<li><a href="#_existing_condition_type_descriptors">8.22.1. Existing condition type descriptors</a></li>
+<li><a href="#_login_event_type">13.23.1. Login event type</a></li>
+<li><a href="#_view_event_type">13.23.2. View event type</a></li>
+<li><a href="#_form_event_type">13.23.3. Form event type</a></li>
+<li><a href="#_update_properties_event_type">13.23.4. Update properties event type</a></li>
+<li><a href="#_identify_event_type">13.23.5. Identify event type</a></li>
+<li><a href="#_session_created_event_type">13.23.6. Session created event type</a></li>
+<li><a href="#_goal_event_type">13.23.7. Goal event type</a></li>
+<li><a href="#_modify_consent_event_type">13.23.8. Modify consent event type</a></li>
 </ul>
 </li>
-<li><a href="#_built_in_action_types">8.23. Built-in action types</a>
+<li><a href="#_built_in_condition_types">13.24. Built-in condition types</a>
 <ul class="sectlevel3">
-<li><a href="#_existing_action_types_descriptors">8.23.1. Existing action types descriptors</a></li>
+<li><a href="#_existing_condition_type_descriptors">13.24.1. Existing condition type descriptors</a></li>
 </ul>
 </li>
-<li><a href="#_updating_events_using_the_context_servlet">8.24. Updating Events Using the Context Servlet</a>
+<li><a href="#_built_in_action_types">13.25. Built-in action types</a>
 <ul class="sectlevel3">
-<li><a href="#_solution">8.24.1. Solution</a></li>
-<li><a href="#_defining_rules">8.24.2. Defining Rules</a></li>
+<li><a href="#_existing_action_types_descriptors">13.25.1. Existing action types descriptors</a></li>
 </ul>
 </li>
+<li><a href="#_updating_events_using_the_context_servlet">13.26. Updating Events Using the Context Servlet</a>
+<ul class="sectlevel3">
+<li><a href="#_solution">13.26.1. Solution</a></li>
+<li><a href="#_defining_rules">13.26.2. Defining Rules</a></li>
 </ul>
 </li>
-<li><a href="#_integration_samples">9. Integration samples</a>
+<li><a href="#_unomi_web_tracker_reference">13.27. Unomi Web Tracker reference</a>
+<ul class="sectlevel3">
+<li><a href="#_custom_events">13.27.1. Custom events</a></li>
+<li><a href="#_integrating_with_tag_managers">13.27.2. Integrating with tag managers</a></li>
+<li><a href="#_cookiesession_handling">13.27.3. Cookie/session handling</a></li>
+<li><a href="#_javascript_api">13.27.4. JavaScript API</a></li>
+</ul>
+</li>
+</ul>
+</li>
+<li><a href="#_integration_samples">14. Integration samples</a>
 <ul class="sectlevel2">
-<li><a href="#_samples">9.1. Samples</a></li>
-<li><a href="#_login_sample">9.2. Login sample</a>
+<li><a href="#_samples">14.1. Samples</a></li>
+<li><a href="#_login_sample">14.2. Login sample</a>
 <ul class="sectlevel3">
-<li><a href="#_warning">9.2.1. Warning !</a></li>
-<li><a href="#_installing_the_samples">9.2.2. Installing the samples</a></li>
+<li><a href="#_warning">14.2.1. Warning !</a></li>
+<li><a href="#_installing_the_samples">14.2.2. Installing the samples</a></li>
 </ul>
 </li>
-<li><a href="#_twitter_sample">9.3. Twitter sample</a>
+<li><a href="#_twitter_sample">14.3. Twitter sample</a>
 <ul class="sectlevel3">
-<li><a href="#_overview">9.3.1. Overview</a></li>
-<li><a href="#_interacting_with_the_context_server">9.3.2. Interacting with the context server</a></li>
-<li><a href="#_retrieving_context_information_from_unomi_using_the_context_servlet">9.3.3. Retrieving context information from Unomi using the context servlet</a></li>
+<li><a href="#_overview">14.3.1. Overview</a></li>
+<li><a href="#_interacting_with_the_context_server">14.3.2. Interacting with the context server</a></li>
+<li><a href="#_retrieving_context_information_from_unomi_using_the_context_servlet">14.3.3. Retrieving context information from Unomi using the context servlet</a></li>
 </ul>
 </li>
-<li><a href="#_example_24">9.4. Example</a>
+<li><a href="#_example_26">14.4. Example</a>
 <ul class="sectlevel3">
-<li><a href="#_html_page">9.4.1. HTML page</a></li>
-<li><a href="#_javascript">9.4.2. Javascript</a></li>
+<li><a href="#_html_page">14.4.1. HTML page</a></li>
+<li><a href="#_javascript">14.4.2. Javascript</a></li>
 </ul>
 </li>
-<li><a href="#_conclusion">9.5. Conclusion</a></li>
-<li><a href="#_annex">9.6. Annex</a></li>
-<li><a href="#_weather_update_sample">9.7. Weather update sample</a></li>
+<li><a href="#_conclusion_2">14.5. Conclusion</a></li>
+<li><a href="#_annex">14.6. Annex</a></li>
+<li><a href="#_weather_update_sample">14.7. Weather update sample</a></li>
 </ul>
 </li>
-<li><a href="#_connectors">10. Connectors</a>
+<li><a href="#_connectors">15. Connectors</a>
 <ul class="sectlevel2">
-<li><a href="#_connectors_2">10.1. Connectors</a>
+<li><a href="#_connectors_2">15.1. Connectors</a>
 <ul class="sectlevel3">
-<li><a href="#_call_for_contributors">10.1.1. Call for contributors</a></li>
+<li><a href="#_call_for_contributors">15.1.1. Call for contributors</a></li>
 </ul>
 </li>
-<li><a href="#_salesforce_connector">10.2. Salesforce Connector</a>
+<li><a href="#_salesforce_connector">15.2. Salesforce Connector</a>
 <ul class="sectlevel3">
-<li><a href="#_getting_started_2">10.2.1. Getting started</a></li>
-<li><a href="#_properties">10.2.2. Properties</a></li>
-<li><a href="#_hot_deploying_updates_to_the_salesforce_connector_for_developers">10.2.3. Hot-deploying updates to the Salesforce connector (for developers)</a></li>
-<li><a href="#_using_the_salesforce_workbench_for_testing_rest_api">10.2.4. Using the Salesforce Workbench for testing REST API</a></li>
-<li><a href="#_setting_up_streaming_push_queries">10.2.5. Setting up Streaming Push queries</a></li>
-<li><a href="#_executing_the_unit_tests">10.2.6. Executing the unit tests</a></li>
+<li><a href="#_getting_started">15.2.1. Getting started</a></li>
+<li><a href="#_properties">15.2.2. Properties</a></li>
+<li><a href="#_hot_deploying_updates_to_the_salesforce_connector_for_developers">15.2.3. Hot-deploying updates to the Salesforce connector (for developers)</a></li>
+<li><a href="#_using_the_salesforce_workbench_for_testing_rest_api">15.2.4. Using the Salesforce Workbench for testing REST API</a></li>
+<li><a href="#_setting_up_streaming_push_queries">15.2.5. Setting up Streaming Push queries</a></li>
+<li><a href="#_executing_the_unit_tests">15.2.6. Executing the unit tests</a></li>
 </ul>
 </li>
-<li><a href="#_mailchimp_connector">10.3. MailChimp Connector</a>
+<li><a href="#_mailchimp_connector">15.3. MailChimp Connector</a>
 <ul class="sectlevel3">
-<li><a href="#_getting_started_3">10.3.1. Getting started</a></li>
+<li><a href="#_getting_started_2">15.3.1. Getting started</a></li>
 </ul>
 </li>
 </ul>
 </li>
-<li><a href="#_developers">11. Developers</a>
+<li><a href="#_developers">16. Developers</a>
 <ul class="sectlevel2">
-<li><a href="#_building">11.1. Building</a>
+<li><a href="#_building">16.1. Building</a>
 <ul class="sectlevel3">
-<li><a href="#_initial_setup">11.1.1. Initial Setup</a></li>
-<li><a href="#_building_2">11.1.2. Building</a></li>
-<li><a href="#_installing_an_elasticsearch_server">11.1.3. Installing an ElasticSearch server</a></li>
-<li><a href="#_deploying_the_generated_binary_package">11.1.4. Deploying the generated binary package</a></li>
-<li><a href="#_deploying_into_an_existing_karaf_server">11.1.5. Deploying into an existing Karaf server</a></li>
-<li><a href="#_jdk_selection_on_mac_os_x">11.1.6. JDK Selection on Mac OS X</a></li>
-<li><a href="#_running_the_integration_tests">11.1.7. Running the integration tests</a></li>
-<li><a href="#_testing_with_an_example_page">11.1.8. Testing with an example page</a></li>
-</ul>
-</li>
-<li><a href="#_ssh_shell_commands">11.2. SSH Shell Commands</a>
-<ul class="sectlevel3">
-<li><a href="#_using_the_shell">11.2.1. Using the shell</a></li>
-<li><a href="#_lifecycle_commands">11.2.2. Lifecycle commands</a></li>
-<li><a href="#_runtime_commands">11.2.3. Runtime commands</a></li>
-</ul>
-</li>
-<li><a href="#_writing_plugins">11.3. Writing Plugins</a></li>
-<li><a href="#_types_vs_instances">11.4. Types vs. instances</a></li>
-<li><a href="#_plugin_structure">11.5. Plugin structure</a></li>
-<li><a href="#_extension_points">11.6. Extension points</a>
-<ul class="sectlevel3">
-<li><a href="#_actiontype">11.6.1. ActionType</a></li>
-<li><a href="#_conditiontype">11.6.2. ConditionType</a></li>
-<li><a href="#_persona_2">11.6.3. Persona</a></li>
-<li><a href="#_propertymergestrategytype">11.6.4. PropertyMergeStrategyType</a></li>
-<li><a href="#_propertytype">11.6.5. PropertyType</a></li>
-<li><a href="#_rule_2">11.6.6. Rule</a></li>
-<li><a href="#_scoring">11.6.7. Scoring</a></li>
-<li><a href="#_segments">11.6.8. Segments</a></li>
-<li><a href="#_tag">11.6.9. Tag</a></li>
-<li><a href="#_valuetype">11.6.10. ValueType</a></li>
-</ul>
-</li>
-<li><a href="#_custom_plugins">11.7. Custom plugins</a>
-<ul class="sectlevel3">
-<li><a href="#_creating_a_plugin">11.7.1. Creating a plugin</a></li>
-<li><a href="#_deployment_and_custom_definition">11.7.2. Deployment and custom definition</a></li>
-<li><a href="#_predefined_segments">11.7.3. Predefined segments</a></li>
-<li><a href="#_predefined_rules">11.7.4. Predefined rules</a></li>
-<li><a href="#_predefined_properties">11.7.5. Predefined properties</a></li>
-<li><a href="#_predefined_child_conditions">11.7.6. Predefined child conditions</a></li>
-<li><a href="#_predefined_personas">11.7.7. Predefined personas</a></li>
-<li><a href="#_custom_action_types">11.7.8. Custom action types</a></li>
-<li><a href="#_custom_condition_types">11.7.9. Custom condition types</a></li>
+<li><a href="#_initial_setup">16.1.1. Initial Setup</a></li>
+<li><a href="#_building_2">16.1.2. Building</a></li>
+<li><a href="#_installing_an_elasticsearch_server">16.1.3. Installing an ElasticSearch server</a></li>
+<li><a href="#_deploying_the_generated_binary_package">16.1.4. Deploying the generated binary package</a></li>
+<li><a href="#_deploying_into_an_existing_karaf_server">16.1.5. Deploying into an existing Karaf server</a></li>
+<li><a href="#_jdk_selection_on_mac_os_x">16.1.6. JDK Selection on Mac OS X</a></li>
+<li><a href="#_running_the_integration_tests">16.1.7. Running the integration tests</a></li>
+<li><a href="#_testing_with_an_example_page">16.1.8. Testing with an example page</a></li>
+</ul>
+</li>
+<li><a href="#_ssh_shell_commands">16.2. SSH Shell Commands</a>
+<ul class="sectlevel3">
+<li><a href="#_using_the_shell">16.2.1. Using the shell</a></li>
+<li><a href="#_lifecycle_commands">16.2.2. Lifecycle commands</a></li>
+<li><a href="#_runtime_commands">16.2.3. Runtime commands</a></li>
+</ul>
+</li>
+<li><a href="#_writing_plugins">16.3. Writing Plugins</a></li>
+<li><a href="#_types_vs_instances">16.4. Types vs. instances</a></li>
+<li><a href="#_plugin_structure">16.5. Plugin structure</a></li>
+<li><a href="#_extension_points">16.6. Extension points</a>
+<ul class="sectlevel3">
+<li><a href="#_actiontype">16.6.1. ActionType</a></li>
+<li><a href="#_conditiontype">16.6.2. ConditionType</a></li>
+<li><a href="#_persona_2">16.6.3. Persona</a></li>
+<li><a href="#_propertymergestrategytype">16.6.4. PropertyMergeStrategyType</a></li>
+<li><a href="#_propertytype">16.6.5. PropertyType</a></li>
+<li><a href="#_rule_2">16.6.6. Rule</a></li>
+<li><a href="#_scoring">16.6.7. Scoring</a></li>
+<li><a href="#_segments">16.6.8. Segments</a></li>
+<li><a href="#_tag">16.6.9. Tag</a></li>
+<li><a href="#_valuetype">16.6.10. ValueType</a></li>
+</ul>
+</li>
+<li><a href="#_custom_plugins">16.7. Custom plugins</a>
+<ul class="sectlevel3">
+<li><a href="#_creating_a_plugin">16.7.1. Creating a plugin</a></li>
+<li><a href="#_deployment_and_custom_definition">16.7.2. Deployment and custom definition</a></li>
+<li><a href="#_predefined_segments">16.7.3. Predefined segments</a></li>
+<li><a href="#_predefined_rules">16.7.4. Predefined rules</a></li>
+<li><a href="#_predefined_properties">16.7.5. Predefined properties</a></li>
+<li><a href="#_predefined_child_conditions">16.7.6. Predefined child conditions</a></li>
+<li><a href="#_predefined_personas">16.7.7. Predefined personas</a></li>
+<li><a href="#_custom_action_types">16.7.8. Custom action types</a></li>
+<li><a href="#_custom_condition_types">16.7.9. Custom condition types</a></li>
 </ul>
 </li>
-<li><a href="#_migration_patches">11.8. Migration patches</a></li>
+<li><a href="#_migration_patches">16.8. Migration patches</a></li>
 </ul>
 </li>
 </ul>
@@ -401,16 +508,266 @@
 </div>
 </div>
 <div class="sect1">
-<h2 id="_quick_start">1. Quick start</h2>
+<h2 id="_whats_new">1. What&#8217;s new</h2>
 <div class="sectionbody">
 <div class="sect2">
-<h3 id="_five_minutes_quickstart">1.1. Five Minutes QuickStart</h3>
+<h3 id="_whats_new_in_apache_unomi_2_0">1.1. What&#8217;s new in Apache Unomi 2.0</h3>
+<div class="paragraph">
+<p>Apache Unomi 2 is a new release focused on improving core functionalities and robustness of the product.</p>
+</div>
+<div class="paragraph">
+<p>The introduction of tighter data validation with JSON Schemas required some changes in the product data model, which presented an opportunity for noticeable improvements in the overall performance.</p>
+</div>
+<div class="paragraph">
+<p>This new release also introduces a first (beta) version of the Unomi GraphQL API.</p>
+</div>
+<div class="sect3">
+<h4 id="_introducing_profiles_aliases">1.1.1. Introducing profiles aliases</h4>
+<div class="paragraph">
+<p>Profiles may now have alias IDs, which is a new way to reference profiles using multiple IDs. The Unomi ID still exists, but a new index with aliases can reference a single Unomi profile. This enables more flexible integrations with external systems, as well as provide more flexible and reliable merging mechanisms. A new REST API makes it easy to define, update and remove aliases for profiles. You can read more about <a href="#_what_profile_aliases_are_and_how_to_use_them">profile aliases here</a>.</p>
+</div>
+</div>
+<div class="sect3">
+<h4 id="_scopes_declarations_are_now_required">1.1.2. Scopes declarations are now required</h4>
+<div class="paragraph">
+<p>Scopes declarations are now required in Unomi 2. When submitting an event and specifying a scope,
+that scope must already be declared on the platform.</p>
+</div>
+<div class="paragraph">
+<p>Scopes can be easily created via the corresponding REST API (<code>cxs/scopes</code>)</p>
+</div>
+<div class="paragraph">
+<p>For example, an "apache" scope can be created using the following API call.</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="highlight"><code>curl --location --request POST 'http://localhost:8181/cxs/scopes' \
+-u 'karaf:karaf' \
+--header 'Content-Type: application/json' \
+--data-raw '{
+"itemId": "apache",
+"itemType": "scope"
+}'</code></pre>
+</div>
+</div>
+</div>
+<div class="sect3">
+<h4 id="_json_schemas">1.1.3. JSON Schemas</h4>
+<div class="paragraph">
+<p>Apache Unomi 2 introduces support for <a href="https://json-schema.org/specification.html">JSON Schema</a> for all of its publicly exposed endpoints.
+Data received by Apache Unomi 2 will first be validated against a known schema to make sure it complies with an expected payload.
+If the received payload does not match a known schema, it will be rejected by Apache Unomi 2.</p>
+</div>
+<div class="paragraph">
+<p>Apache Unomi 2 also introduces a set of administrative endpoints allowing new schemas and/or schemas extensions to be registered.</p>
+</div>
+<div class="paragraph">
+<p>More details about JSON Schemas implementation are available in the <a href="#_json_schemas_2">corresponding section</a> of the documentation.</p>
+</div>
+</div>
+<div class="sect3">
+<h4 id="_updated_data_model">1.1.4. Updated data model</h4>
+<div class="paragraph">
+<p>The introduction of JSON schema required us to modify Apache Unomi data model, One of the key differences is the removal of open maps.</p>
+</div>
+<div class="paragraph">
+<p>The properties field in the events objects provided by unomi are now restricted by JSON schema.
+This means object properties must be declared in a JSON schema for an event to be accepted.</p>
+</div>
+<div class="paragraph">
+<p>A new property, flattenedProperties has been introduced to the event object, this property has been added to store the properties as
+flattened in Elasticsearch and should avoid mapping explosion for dynamic properties.</p>
+</div>
+<div class="paragraph">
+<p>If there is dynamic properties that you want to send with your event, you should use the flattenedProperties field of the event.</p>
+</div>
+<div class="paragraph">
+<p>It&#8217;s also necessary to specify the format of the values which are added to flattenedProperties by JSON schema but these value will be
+stored as flattened and will not create dynamic mapping contrary to the properties field of the events.</p>
+</div>
+<div class="paragraph">
+<p>Here is an example for objects that used dynamic properties for URL parameters:</p>
+</div>
+<div class="paragraph">
+<p>The following event example in Apache Unomi 1.x:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="highlight"><code>{
+    "eventType":"view",
+    "scope":"digitall",
+    "properties":{
+        "URLParameters":{
+           "utm_source":"source"
+        }
+    },
+    "target":{
+        "scope":"digitall",
+        "itemId":"30c0a9e3-4330-417d-9c66-4c1beec85f08",
+        "itemType":"page",
+        "properties":{
+           "pageInfo":{
+              "pageID":"30c0a9e3-4330-417d-9c66-4c1beec85f08",
+              "nodeType":"jnt:page",
+              "pageName":"Home",
+              ...
+           },
+           "attributes":{},
+           "consentTypes":[]
+        }
+    },
+    "source":{
+        "scope":"digitall",
+        "itemId":"ff5886e0-d75a-4061-9de9-d90dfc9e18d8",
+        "itemType":"site"
+    }
+}</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>Is replaced by the following in Apache Unomi 2.x:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="highlight"><code>{
+    "eventType":"view",
+    "scope":"digitall",
+    "flattenedProperties":{
+        "URLParameters":{
+           "utm_source":"source"
+        }
+    },
+    "target":{
+        "scope":"digitall",
+        "itemId":"30c0a9e3-4330-417d-9c66-4c1beec85f08",
+        "itemType":"page",
+        "properties":{
+           "pageInfo":{
+              "pageID":"30c0a9e3-4330-417d-9c66-4c1beec85f08",
+              "nodeType":"jnt:page",
+              "pageName":"Home",
+              ...
+           },
+           "attributes":{},
+           "consentTypes":[]
+        }
+    },
+    "source":{
+        "scope":"digitall",
+        "itemId":"ff5886e0-d75a-4061-9de9-d90dfc9e18d8",
+        "itemType":"site"
+    }
+}</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>If using the default Apache 1.x data model, our Unomi 2 migration process will handle the data model changes for you.</p>
+</div>
+<div class="paragraph">
+<p>If you are using custom events/objects, please refer to the detailed <a href="#_migration_overview">migration guide</a> for more details.</p>
+</div>
+</div>
+<div class="sect3">
+<h4 id="_new_web_tracker">1.1.5. New Web Tracker</h4>
+<div class="paragraph">
+<p>Apache Unomi 2.0 Web Tracker, located in <code>extensions/web-tracker/</code> has been completely rewritten. It is no longer based on an external library and is fully self-sufficient. It is based on an external contribution that has been used in production on many sites.</p>
+</div>
+<div class="paragraph">
+<p>You can find more information about the <a href="#_unomi_web_tracking_tutorial">new web tracker here</a>.</p>
+</div>
+</div>
+<div class="sect3">
+<h4 id="_graphql_api_beta">1.1.6. GraphQL API - beta</h4>
+<div class="paragraph">
+<p>Apache Unomi 2.0 sees the introduction of a new (beta) GraphQL API.
+Available behind a feature flag (the API disabled by default), the GraphQL API is available for you to play with.</p>
+</div>
+<div class="paragraph">
+<p>More details about how to enable/disable the GraphQL API are available in the <a href="#_graphql_api">corresponding section</a> of the documentation.</p>
+</div>
+<div class="paragraph">
+<p>We welcome tickets/PRs to improve its robustness and progressively make it ready for prime time.</p>
+</div>
+</div>
+<div class="sect3">
+<h4 id="_migrate_from_unomi_1_x">1.1.7. Migrate from Unomi 1.x</h4>
+<div class="paragraph">
+<p>To facilitate migration we prepared a set of scripts that will automatically handle the migration of your data from Apache Unomi 1.5+ to Apache Unomi 2.0.</p>
+</div>
+<div class="paragraph">
+<p>It is worth keeping in mind that for Apache Unomi 2.0 we do not support “hot” migration,
+the migration process will require a shutdown of your cluster to guarantee that no new events will be collected while data migration is in progress.</p>
+</div>
+<div class="paragraph">
+<p>Special caution must be taken if you declared custom events as our migration scripts can only handle objects we know of.
+More details about migration (incl. of custom events) is available in the corresponding section <a href="#_migrations">corresponding section</a> of the documentation.</p>
+</div>
+</div>
+<div class="sect3">
+<h4 id="_elasticsearch_compatibility">1.1.8. Elasticsearch compatibility</h4>
+<div class="paragraph">
+<p>We currently recommend using Elasticsearch 7.17.5 with Apache Unomi 2.0,
+this ensure you are on a recent version that is not impacted by the log4j vulnerabilities (fixed in Elasticsearch 7.16.3).</p>
+</div>
+<div class="paragraph">
+<p>This version increase is releated to Apache Unomi 2.0 makeing use of a new Elasticsearch field type
+called <a href="https://www.elastic.co/guide/en/elasticsearch/reference/7.17/flattened.html">Flattened</a>,
+and although it was available in prior versions of Elasticsearch, we do not recommend using those
+due to the above-mentioned log4j vulnerabilities.</p>
+</div>
+</div>
+</div>
+</div>
+</div>
+<div class="sect1">
+<h2 id="_discover_unomi">2. Discover Unomi</h2>
+<div class="sectionbody">
+<div class="sect2">
+<h3 id="_quick_start_with_docker">2.1. Quick start with Docker</h3>
+<div class="paragraph">
+<p>Begin by creating a <code>docker-compose.yml</code> file with the following content:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="highlight"><code>version: '3.8'
+services:
+    elasticsearch:
+    image: docker.elastic.co/elasticsearch/elasticsearch:7.17.5
+    environment:
+        - discovery.type=single-node
+    ports:
+        - 9200:9200
+    unomi:
+    # Unomi version can be updated based on your needs
+    image: apache/unomi:2.0.0
+    environment:
+        - UNOMI_ELASTICSEARCH_ADDRESSES=elasticsearch:9200
+        - UNOMI_THIRDPARTY_PROVIDER1_IPADDRESSES=0.0.0.0/0,::1,127.0.0.1
+    ports:
+        - 8181:8181
+        - 9443:9443
+        - 8102:8102
+    links:
+        - elasticsearch
+    depends_on:
+        - elasticsearch</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>From the same folder, start the environment using <code>docker-compose up</code> and wait for the startup to complete.</p>
+</div>
+<div class="paragraph">
+<p>Try accessing <a href="https://localhost:9443/cxs/cluster" class="bare">https://localhost:9443/cxs/cluster</a> with username/password: karaf/karaf . You might get a certificate warning in your browser, just accept it despite the warning it is safe.</p>
+</div>
+</div>
+<div class="sect2">
+<h3 id="_quick_start_manually">2.2. Quick Start manually</h3>
 <div class="paragraph">
 <p>1) Install JDK 8 (<a href="https://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html" class="bare">https://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html</a>) and make sure you set the
 JAVA_HOME variable <a href="https://docs.oracle.com/cd/E19182-01/820-7851/inst_cli_jdk_javahome_t/" class="bare">https://docs.oracle.com/cd/E19182-01/820-7851/inst_cli_jdk_javahome_t/</a> (see our <a href="#_jdk_compatibility">Getting Started</a> guide for more information on JDK compatibility)</p>
 </div>
 <div class="paragraph">
-<p>2) Download ElasticSearch here : <a href="https://www.elastic.co/downloads/past-releases/elasticsearch-7-4-2" class="bare">https://www.elastic.co/downloads/past-releases/elasticsearch-7-4-2</a> (please &lt;strong&gt;make sure&lt;/strong&gt; you use the proper version : 7.4.2)</p>
+<p>2) Download ElasticSearch here : <a href="https://www.elastic.co/downloads/past-releases/elasticsearch-7-17-5" class="bare">https://www.elastic.co/downloads/past-releases/elasticsearch-7-17-5</a> (please &lt;strong&gt;make sure&lt;/strong&gt; you use the proper version : 7.17.5)</p>
 </div>
 <div class="paragraph">
 <p>3) Uncompress it and change the <code>config/elasticsearch.yml</code> to include the following config : &lt;code&gt;cluster.name: contextElasticSearch&lt;/code&gt;</p>
@@ -434,7 +791,7 @@ JAVA_HOME variable <a href="https://docs
 <p>9) Try accessing <a href="https://localhost:9443/cxs/cluster" class="bare">https://localhost:9443/cxs/cluster</a> with username/password: <code>karaf/karaf</code> . You might get a certificate warning in your browser, just accept it despite the warning it is safe.</p>
 </div>
 <div class="paragraph">
-<p>10) Request your first context by simply accessing : <a href="http://localhost:8181/context.js?sessionId=1234" class="bare">http://localhost:8181/context.js?sessionId=1234</a></p>
+<p>10) Request your first context by simply accessing : <a href="http://localhost:8181/cxs/context.js?sessionId=1234" class="bare">http://localhost:8181/cxs/context.js?sessionId=1234</a></p>
 </div>
 <div class="paragraph">
 <p>11) If something goes wrong, you should check the logs in <code>./data/log/karaf.log</code>. If you get errors on ElasticSearch,
@@ -446,30 +803,19 @@ make sure you are using the proper versi
 <div class="ulist">
 <ul>
 <li>
-<p>Connect to <a href="http://localhost:8181" class="bare">http://localhost:8181</a> to try our some live examples (such as the web tracker)</p>
-</li>
-<li>
 <p>Trying our integration <a href="#_samples">samples page</a></p>
 </li>
-<li>
-<p>Learning more about the <a href="#_web_tracker">web tracker</a></p>
-</li>
 </ul>
 </div>
 </div>
-</div>
-</div>
-<div class="sect1">
-<h2 id="_first_steps_with_apache_unomi">2. First steps with Apache Unomi</h2>
-<div class="sectionbody">
 <div class="sect2">
-<h3 id="_getting_started_with_unomi">2.1. Getting started with Unomi</h3>
+<h3 id="_getting_started_with_unomi">2.3. Getting started with Unomi</h3>
 <div class="paragraph">
 <p>We will first get you up and running with an example. We will then lift the corner of the cover somewhat and explain
 in greater details what just happened.</p>
 </div>
 <div class="sect3">
-<h4 id="_prerequisites">2.1.1. Prerequisites</h4>
+<h4 id="_prerequisites">2.3.1. Prerequisites</h4>
 <div class="paragraph">
 <p>This document assumes working knowledge of <a href="https://git-scm.com/">git</a> to be able to retrieve the code for Unomi and the example.
 Additionally, you will require a working Java 8 or above install. Refer to <a href="http://www.oracle.com/technetwork/java/javase/">http://www.oracle.com/technetwork/java/javase/</a> for details on how to download and install Java SE 8 or greater.</p>
@@ -489,22 +835,20 @@ them at your own risks.</p>
 </div>
 </div>
 <div class="sect4">
-<h5 id="_elasticsearch_compatibility">ElasticSearch compatibility</h5>
+<h5 id="_elasticsearch_compatibility_2">ElasticSearch compatibility</h5>
 <div class="paragraph">
-<p>Starting with version 1.5.0 Apache Unomi adds compatibility with ElasticSearch 7.4 . It is highly recommended to use the
-ElasticSearch version provided by the documentation when possible. However minor versions (7.4.x) should also work, and
-one version higher (7.5) will usually work. Going higher than that is risky given the way that ElasticSearch is developed
-and breaking changes are introduced quite often. If in doubt, don&#8217;t hesitate to check with the Apache Unomi community
+<p>Starting with version 2.0.0 Apache Unomi adds compatibility with ElasticSearch 7.17.5 . It is highly recommended to use the
+ElasticSearch version specified in the documentation whenever possible. If in doubt, don&#8217;t hesitate to check with the Apache Unomi community
 to get the latest information about ElasticSearch version compatibility.</p>
 </div>
 </div>
 </div>
 <div class="sect3">
-<h4 id="_running_unomi">2.1.2. Running Unomi</h4>
+<h4 id="_running_unomi">2.3.2. Running Unomi</h4>
 <div class="sect4">
 <h5 id="_start_unomi">Start Unomi</h5>
 <div class="paragraph">
-<p>Start Unomi according to the <a href="#_five_minutes_quickstart">five minutes quick start</a> or by compiling using the
+<p>Start Unomi according to the <a href="#Five Minutes QuickStart">quick start with docker</a> or by compiling using the
 <a href="#_building">building instructions</a>. Once you have Karaf running,
  you should wait until you see the following messages on the Karaf console:</p>
 </div>
@@ -522,7 +866,7 @@ Initializing cluster service endpoint...
 </div>
 <div class="paragraph">
 <p>This indicates that all the Unomi services are started and ready to react to requests. You can then open a browser and go to <code><a href="http://localhost:8181/cxs" class="bare">http://localhost:8181/cxs</a></code> to see the list of
-available RESTful services or retrieve an initial context at <code><a href="http://localhost:8181/context.json" class="bare">http://localhost:8181/context.json</a></code> (which isn&#8217;t very useful at this point).</p>
+available RESTful services or retrieve an initial context at <code><a href="http://localhost:8181/cxs/context.json" class="bare">http://localhost:8181/cxs/context.json</a></code> (which isn&#8217;t very useful at this point).</p>
 </div>
 <div class="paragraph">
 <p>You can now find an introduction page at the following location: <a href="http://localhost:8181" class="bare">http://localhost:8181</a></p>
@@ -536,18 +880,486 @@ requests you can do once your server is
 </div>
 </div>
 <div class="sect2">
-<h3 id="_recipes">2.2. Recipes</h3>
+<h3 id="_unomi_web_tracking_tutorial">2.4. Unomi web tracking tutorial</h3>
+<div class="paragraph">
+<p>In this tutorial we will guide through the basic steps of getting started with a web tracking project. You will see how to integrate the built-in web tracker with an existing web site and what this enables.</p>
+</div>
+<div class="paragraph">
+<p>If you prefer to use existing HTML and Javascript rather than building your own, all the code we feature in this tutorial is extracted from our tracker sample which is available here: <a href="https://github.com/apache/unomi/blob/master/extensions/web-tracker/wab/src/main/webapp/index.html" class="bare">https://github.com/apache/unomi/blob/master/extensions/web-tracker/wab/src/main/webapp/index.html</a> . However you will still need to use the REST API calls to create the scope and rule to make it all work.</p>
+</div>
 <div class="sect3">
-<h4 id="_introduction">2.2.1. Introduction</h4>
+<h4 id="_installing_the_web_tracker_in_a_web_page">2.4.1. Installing the web tracker in a web page</h4>
+<div class="paragraph">
+<p>Using the built-in tracker is pretty simple, simply add the following code to your HTML page :</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="highlight"><code class="language-javascript" data-lang="javascript">    &lt;script type="text/javascript" src="/tracker/unomi-web-tracker.min.js"&gt;&lt;/script&gt;</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>or you can also use the non-minified version that is available here:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="highlight"><code class="language-javascript" data-lang="javascript">    &lt;script type="text/javascript" src="/tracker/unomi-web-tracker.js"&gt;&lt;/script&gt;</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>This will only load the tracker. To initialize it use a snipper like the following code:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="highlight"><code class="language-javascript" data-lang="javascript">    &lt;script type="text/javascript"&gt;
+        (function () {
+            const unomiTrackerTestConf = {
+                "scope": "unomi-tracker-test",
+                "site": {
+                    "siteInfo": {
+                        "siteID": "unomi-tracker-test"
+                    }
+                },
+                "page": {
+                    "pageInfo": {
+                        "pageID": "unomi-tracker-test-page",
+                        "pageName": document.title,
+                        "pagePath": document.location.pathname,
+                        "destinationURL": document.location.origin + document.location.pathname,
+                        "language": "en",
+                        "categories": [],
+                        "tags": []
+                    },
+                    "attributes": {},
+                    "consentTypes": []
+                },
+                "events:": [],
+                "wemInitConfig": {
+                    "contextServerUrl": document.location.origin,
+                    "timeoutInMilliseconds": "1500",
+                    "contextServerCookieName": "context-profile-id",
+                    "activateWem": true,
+                    "trackerSessionIdCookieName": "unomi-tracker-test-session-id",
+                    "trackerProfileIdCookieName": "unomi-tracker-test-profile-id"
+                }
+            }
+
+            // generate a new session
+            if (unomiWebTracker.getCookie(unomiTrackerTestConf.wemInitConfig.trackerSessionIdCookieName) == null) {
+                unomiWebTracker.setCookie(unomiTrackerTestConf.wemInitConfig.trackerSessionIdCookieName, unomiWebTracker.generateGuid(), 1);
+            }
+
+            // init tracker with our conf
+            unomiWebTracker.initTracker(unomiTrackerTestConf);
+
+            unomiWebTracker._registerCallback(() =&gt; {
+                console.log("Unomi tracker test successfully loaded context", unomiWebTracker.getLoadedContext());
+            }, 'Unomi tracker test callback example');
+
+            // start the tracker
+            unomiWebTracker.startTracker();
+        })();
+    &lt;/script&gt;</code></pre>
+</div>
+</div>
+</div>
+<div class="sect3">
+<h4 id="_creating_a_scope_to_collect_the_data">2.4.2. Creating a scope to collect the data</h4>
+<div class="paragraph">
+<p>You might notice the <code>scope</code> used in the snippet. All events sent to Unomi must be associated with a scope, that must have been created before events are accepted. So in order to make sure the events are collected with the above Javascript code, we must create a scope with the following request.</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="highlight"><code class="language-shell" data-lang="shell">curl --location --request POST 'http://localhost:8181/cxs/scopes' \
+  --header 'Authorization: Basic a2FyYWY6a2FyYWY=' \
+  --header 'Content-Type: application/json' \
+  --data-raw '{
+    "itemId": "unomi-tracker-test",
+    "metadata": {
+      "id": "unomi-tracker-test",
+      "name": "Unomi tracker Test Scope"
+    }
+  }'</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>The authorization is the default username/password for the REST API, which is <code>karaf:karaf</code> and you that should definitely be changed as soon as possible by modifying the <code>etc/users.properties</code> file.</p>
+</div>
+</div>
+<div class="sect3">
+<h4 id="_using_tracker_in_your_own_javascript_projects">2.4.3. Using tracker in your own JavaScript projects</h4>
+<div class="paragraph">
+<p>The tracker also exists as an NPM library that you can integrate with your own Javascript projects. You can find the library here:</p>
+</div>
+<div class="literalblock">
+<div class="content">
+<pre>https://www.npmjs.com/package/apache-unomi-tracker</pre>
+</div>
+</div>
+<div class="paragraph">
+<p>Here&#8217;s an example on how to use it:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="highlight"><code class="language-shell" data-lang="shell">    yarn add apache-unomi-tracker</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>You can then simply use it in your JS code using something like this:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="highlight"><code class="language-javascript" data-lang="javascript">import {useTracker} from "apache-unomi-tracker";
+
+(function () {
+    const unomiWebTracker = useTracker();
+    const unomiTrackerTestConf = {
+        "scope": "unomi-tracker-test",
+        "site": {
+            "siteInfo": {
+                "siteID": "unomi-tracker-test"
+            }
+        },
+        "page": {
+            "pageInfo": {
+                "pageID": "unomi-tracker-test-page",
+                "pageName": document.title,
+                "pagePath": document.location.pathname,
+                "destinationURL": document.location.origin + document.location.pathname,
+                "language": "en",
+                "categories": [],
+                "tags": []
+            },
+            "attributes": {},
+            "consentTypes": []
+        },
+        "events:": [],
+        "wemInitConfig": {
+            "contextServerUrl": document.location.origin,
+            "timeoutInMilliseconds": "1500",
+            "contextServerCookieName": "context-profile-id",
+            "activateWem": true,
+            "trackerSessionIdCookieName": "unomi-tracker-test-session-id",
+            "trackerProfileIdCookieName": "unomi-tracker-test-profile-id"
+        }
+    }
+
+    // generate a new session
+    if (unomiWebTracker.getCookie(unomiTrackerTestConf.wemInitConfig.trackerSessionIdCookieName) == null) {
+        unomiWebTracker.setCookie(unomiTrackerTestConf.wemInitConfig.trackerSessionIdCookieName, unomiWebTracker.generateGuid(), 1);
+    }
+
+    // init tracker with our conf
+    unomiWebTracker.initTracker(unomiTrackerTestConf);
+
+    unomiWebTracker._registerCallback(() =&gt; {
+        console.log("Unomi tracker test successfully loaded context", unomiWebTracker.getLoadedContext());
+    }, 'Unomi tracker test callback example');
+
+    // start the tracker
+    unomiWebTracker.startTracker();
+})();</code></pre>
+</div>
+</div>
+</div>
+<div class="sect3">
+<h4 id="_viewing_collected_events">2.4.4. Viewing collected events</h4>
+<div class="paragraph">
+<p>There are multiple ways to view the events that were received. For example, you could use the following cURL request:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="highlight"><code class="language-shell" data-lang="shell">curl --location --request POST 'http://localhost:8181/cxs/events/search' \
+  --header 'Authorization: Basic a2FyYWY6a2FyYWY=' \
+  --header 'Content-Type: application/json' \
+  --data-raw '{
+    "sortby" : "timeStamp:desc",
+    "condition" : {
+      "type" : "matchAllCondition"
+    }
+  }'</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>Another (powerful) way to look at events is to use the SSH Console. You can connect to it with the following shell command:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="highlight"><code class="language-shell" data-lang="shell">    ssh -p 8102 karaf@localhost</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>Using the same username password (karaf:karaf) and then you can use command such as :</p>
+</div>
+<div class="ulist">
+<ul>
+<li>
+<p><code>event-tail</code> to view in realtime the events as they come in (CTRL+C to stop)</p>
+</li>
+<li>
+<p><code>event-list</code> to view the latest events</p>
+</li>
+<li>
+<p><code>event-view EVENT_ID</code> to view the details of a specific event</p>
+</li>
+</ul>
+</div>
+</div>
+<div class="sect3">
+<h4 id="_viewing_the_current_profile">2.4.5. Viewing the current profile</h4>
+<div class="paragraph">
+<p>By default, Unomi uses a cookie called context-profile-id to keep track of the current profile. You can use this the value of this cookie which contains a UUID to lookup the details of the profile. For example with the SSH console you can simply to:</p>
+</div>
+<div class="literalblock">
+<div class="content">
+<pre>profile-view PROFILE_UUID</pre>
+</div>
+</div>
+<div class="paragraph">
+<p>Which will print out the details of the profile with the associated ID.
+Another interesting command is <code>profile-list</code> to list all the recently modified profiles</p>
+</div>
+<div class="paragraph">
+<p>You could also retrieve the profile details using the REST API by using a request such as this one:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="highlight"><code class="language-shell" data-lang="shell">curl --location --request GET 'http://localhost:8181/cxs/profiles/PROFILE_UUID' \
+--header 'Authorization: Basic a2FyYWY6a2FyYWY=' \</code></pre>
+</div>
+</div>
+</div>
+<div class="sect3">
+<h4 id="_adding_a_rule">2.4.6. Adding a rule</h4>
+<div class="paragraph">
+<p>Rules are a powerful ways to react in real-time to incoming events. For example a rule could update a profile when a certain event comes in, either copying values from the event or performing some kind of computation when the event occurs, including accessing remote systems such as a Salesforce CRM (see the Salesforce connector sample).</p>
+</div>
+<div class="paragraph">
+<p>In this example we will simply setup a basic rule that will react to the <code>view</code> event and set a property in the current profile.</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="highlight"><code class="language-shell" data-lang="shell">curl --location --request POST 'http://localhost:8181/cxs/rules' \
+--header 'Authorization: Basic a2FyYWY6a2FyYWY=' \
+--header 'Content-Type: application/json' \
+--data-raw '{
+    "metadata": {
+        "id": "viewEventRule",
+        "name": "View event rule",
+        "description": "Increments a property on a profile to indicate that this rule executed successfully when a view event occurs"
+    },
+    "condition": {
+        "type": "eventTypeCondition",
+        "parameterValues": {
+            "eventTypeId": "view"
+        }
+    },
+    "actions": [
+        {
+            "type": "incrementPropertyAction",
+            "parameterValues": {
+                "propertyName": "pageViewCount"
+            }
+        }
+    ]
+}'</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>The above rule will execute when a view event is received (which is automatically sent by the tracker when a page is loaded) and increments a property called <code>pageViewCount</code> on the user&#8217;s profile.</p>
+</div>
+<div class="paragraph">
+<p>You can then reload then page and check with the <code>profile-view PROFILE_UUID</code> SSH command that the profile was updated with the new property and that it is incremented on each page reload.</p>
+</div>
+<div class="paragraph">
+<p>You can also use the <code>rule-list</code> command to display all the rules in the system and the <code>rule-tail</code> to watch in real-time which rules are executed. The <code>rule-view RULE_ID</code> command will let you view the contents of a rule.</p>
+</div>
+</div>
+<div class="sect3">
+<h4 id="_adding_personalization">2.4.7. Adding personalization</h4>
+<div class="paragraph">
+<p>The last step is to use the newly added property to the profile to perform some page personalization. In order to do that we will use the tracker&#8217;s API to register a personalization that will be using a condition that checks if the <code>pageViewCount</code> is higher than 5. If it has, <code>variant1</code> will be displayed, otherwise the fallback variant <code>variant2</code> will be used instead.</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="highlight"><code class="language-javascript" data-lang="javascript">            variants = {
+                "var1" : {
+                    content : "variant1",
+                },
+                "var2" : {
+                    content : "variant2",
+                }
+            }
+            unomiWebTracker.registerPersonalizationObject({
+                "id": "testPersonalization",
+                "strategy": "matching-first",
+                "strategyOptions": {"fallback": "var2"},
+                "contents": [{
+                    "id": "var1",
+                    "filters": [{
+                        "condition": {
+                            "type": "profilePropertyCondition",
+                            "parameterValues": {
+                                "propertyName" : "properties.pageViewCount.&lt;scope&gt;",
+                                "comparisonOperator" : "greaterThan",
+                                "propertyValueInteger" : 5
+                            }
+                        }
+                    }]
+                }, {
+                    "id": "var2"
+                }]
+            }, variants, false, function (successfulFilters, selectedFilter) {
+                if (selectedFilter) {
+                    document.getElementById(selectedFilter.content).style.display = '';
+                }
+            });</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>As you can see in the above code snippet, a <code>variants</code> array is created with two objects that associated personalization IDs with content IDs. Then we build the personalization object that contains the two IDs and their associated conditions (only a condition on <code>var1</code> is passed in this case) as well as an option to indicate which is the fallback variant in case no conditions are matched.</p>
+</div>
+<div class="paragraph">
+<p>The HTML part of this example looks like this:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="highlight"><code class="language-html" data-lang="html">    &lt;div id="variant1" style="display: none"&gt;
+        You have already seen this page 5 times
+    &lt;/div&gt;
+    &lt;div id="variant2" style="display: none"&gt;
+        Welcome. Please reload this page 5 times until it triggers the personalization change
+    &lt;/div&gt;</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>As you can see we hide the variants by default so that there is no "flashing" effect and then use the callback function to display to variant resolve by Unomi&#8217;s personalization engine.</p>
+</div>
+</div>
+<div class="sect3">
+<h4 id="_conclusion">2.4.8. Conclusion</h4>
+<div class="paragraph">
+<p>What have we achieved so far ?</p>
+</div>
+<div class="ulist">
+<ul>
+<li>
+<p>Installed a tracker in a web page</p>
+</li>
+<li>
+<p>Created a scope in which to collect the data</p>
+</li>
+<li>
+<p>Learned how to use the tracker as an NPM library</p>
+</li>
+<li>
+<p>How to view the collected events</p>
+</li>
+<li>
+<p>How to view the current visitor profile</p>
+</li>
+<li>
+<p>How to add a rule to update a profile property</p>
+</li>
+<li>
+<p>How to personalize a web page&#8217;s content based on the property updated by the rule</p>
+</li>
+</ul>
+</div>
+<div class="paragraph">
+<p>Of course this tutorial is just one example of what could be achieved, and hasn&#8217;t even yet introduced more advanced notions such as profile segmentation or Groovy action scripting. The system is capable of much more, for example by directly using its actions to integrate with third-party systems (CRM, social networks, etc..)</p>
+</div>
+</div>
+<div class="sect3">
+<h4 id="_next_steps">2.4.9. Next steps</h4>
+<div class="ulist">
+<ul>
+<li>
+<p>Learn more about the <a href="#_unomi_web_tracker_reference">web tracker, custom events, API, &#8230;&#8203;</a></p>
+</li>
+<li>
+<p>Learn more about <a href="#_segment">segmentation</a></p>
+</li>
+<li>
+<p>View some more <a href="#_integration_samples">samples</a></p>
+</li>
+<li>
+<p>Continue reading Unomi&#8217;s user manual to see all that is possible with this technology</p>
+</li>
+</ul>
+</div>
+</div>
+</div>
+</div>
+</div>
+<div class="sect1">
+<h2 id="_apache_unomi_recipes_and_requests">3. Apache Unomi Recipes and requests</h2>
+<div class="sectionbody">
+<div class="sect2">
+<h3 id="_recipes">3.1. Recipes</h3>
+<div class="sect3">
+<h4 id="_introduction">3.1.1. Introduction</h4>
 <div class="paragraph">
 <p>In this section of the documentation we provide quick recipes focused on helping you achieve a specific result with
 Apache Unomi.</p>
 </div>
 </div>
 <div class="sect3">
-<h4 id="_how_to_read_a_profile">2.2.2. How to read a profile</h4>
+<h4 id="_enabling_debug_mode">3.1.2. Enabling debug mode</h4>
+<div class="paragraph">
+<p>Although the examples provided in this documentation are correct (they will work "as-is"),
+you might be tempted to modify them to fit your use case, which might result in errors.</p>
+</div>
+<div class="paragraph">
+<p>The best approach during development is to enable Apache Unomi debug mode, which will provide
+you with more detailed logs about events processing.</p>
+</div>
+<div class="paragraph">
+<p>The debug mode can be activated via the karaf SSH console (default credentials are karaf/karaf):</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="highlight"><code>ubuntu@ip-10-0-3-252:~/$ ssh -p 8102 karaf@localhost
+Password authentication
+Password:
+        __ __                  ____
+       / //_/____ __________ _/ __/
+      / ,&lt;  / __ `/ ___/ __ `/ /_
+     / /| |/ /_/ / /  / /_/ / __/
+    /_/ |_|\__,_/_/   \__,_/_/
+
+  Apache Karaf (4.2.15)
+
+Hit '&lt;tab&gt;' for a list of available commands
+and '[cmd] --help' for help on a specific command.
+Hit 'system:shutdown' to shutdown Karaf.
+Hit '&lt;ctrl-d&gt;' or type 'logout' to disconnect shell from current session.
+
+karaf@root()&gt; log:set DEBUG org.apache.unomi.schema.impl.SchemaServiceImpl</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>You can then either watch the logs via your preferred logging mechanism (docker logs, log file, &#8230;&#8203;) or
+simply tail the logs to the terminal you used to enable debug mode.</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="highlight"><code>karaf@root()&gt; log:tail
+08:55:28.128 DEBUG [qtp1422628821-128] Schema validation found 2 errors while validating against schema: https://unomi.apache.org/schemas/json/events/view/1-0-0
+08:55:28.138 DEBUG [qtp1422628821-128] Validation error: There are unevaluated properties at following paths $.source.properties
+08:55:28.140 DEBUG [qtp1422628821-128] Validation error: There are unevaluated properties at following paths $.source.itemId, $.source.itemType, $.source.scope, $.source.properties
+08:55:28.142 ERROR [qtp1422628821-128] An event was rejected - switch to DEBUG log level for more information</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>The example above shows schema validation failure at the <code>$.source.properties</code> path.
+Note that the validation will output one log line for the exact failing path and a log line for its parent,
+therefore to find the source of a schema validation issue it&#8217;s best to start from the top.</p>
+</div>
+</div>
+<div class="sect3">
+<h4 id="_how_to_read_a_profile">3.1.3. How to read a profile</h4>
 <div class="paragraph">
-<p>The simplest way to retrieve profile data for the current profile is to simply send a request to the /context.json
+<p>The simplest way to retrieve profile data for the current profile is to simply send a request to the /cxs/context.json
 endpoint. However you will need to send a body along with that request. Here&#8217;s an example:</p>
 </div>
 <div class="paragraph">
@@ -555,10 +1367,9 @@ endpoint. However you will need to send
 </div>
 <div class="listingblock">
 <div class="content">
-<pre class="highlight"><code>curl -X POST http://localhost:8181/context.json?sessionId=1234 \
+<pre class="highlight"><code>curl -X POST http://localhost:8181/cxs/context.json?sessionId=1234 \
 -H "Content-Type: application/json" \
--d @- &lt;&lt;'EOF'
-{
+--data-raw '{
     "source": {
         "itemId":"homepage",
         "itemType":"page",
@@ -568,24 +1379,23 @@ endpoint. However you will need to send
     "requiredSessionProperties":["*"],
     "requireSegments":true,
     "requireScores":true
-}
-EOF</code></pre>
+}'</code></pre>
 </div>
 </div>
 <div class="paragraph">
 <p>The <code>requiredProfileProperties</code> and <code>requiredSessionProperties</code> are properties that take an array of property names
 that should be retrieved. In this case we use the wildcard character '*' to say we want to retrieve all the available
-properties. The structure of the JSON object that you should send is a JSON-serialized version of the <a href="http://unomi.apache.org/unomi-api/apidocs/org/apache/unomi/api/ContextRequest.html">ContextRequest</a>
-Java class.</p>
+properties. The structure of the JSON object that you should send is a JSON-serialized version of the
+<a href="http://unomi.apache.org/unomi-api/apidocs/org/apache/unomi/api/ContextRequest.html">ContextRequest</a> Java class.</p>
 </div>
 <div class="paragraph">
 <p>Note that it is also possible to access a profile&#8217;s data through the /cxs/profiles/ endpoint but that really should be
-reserved to administrative purposes. All public accesses should always use the /context.json endpoint for consistency
+reserved to administrative purposes. All public accesses should always use the /cxs/context.json endpoint for consistency
 and security.</p>
 </div>
 </div>
 <div class="sect3">
-<h4 id="_how_to_update_a_profile_from_the_public_internet">2.2.3. How to update a profile from the public internet</h4>
+<h4 id="_how_to_update_a_profile_from_the_public_internet">3.1.4. How to update a profile from the public internet</h4>
 <div class="paragraph">
 <p>Before we get into how to update a profile directly from a request coming from the public internet, we&#8217;ll quickly talk
 first about how NOT to do it, because we often see users using the following anti-patterns.</p>
@@ -625,10 +1435,8 @@ ones that you might not want to be overr
 <ul>
 <li>
 <p>(Preferred) Use you own custom event(s) to send data you want to be inserted in a profile, and use rules to map the
-event data to the profile. This is simpler than it sounds, as usually all it requires is setting up a simple rule and
-you&#8217;re ready to update profiles using events. This is also the safest way to update a profile because if you design your
-events to be as specific as possible to your needs, only the data that you specified will be copied to the profile,
-making sure that even in the case an attacker tries to send more data using your custom event it will simply be ignored.</p>
+event data to the profile. This is simpler than it sounds, as usually all it requires is setting up a simple rule,
+defining the corresponding JSON schema and you&#8217;re ready to update profiles using events.</p>
 </li>
 <li>
 <p>Use the protected built-in "updateProperties" event. This event is designed to be used for administrative purposes
@@ -647,8 +1455,7 @@ tracing.</p>
 <pre class="highlight"><code>curl -X POST http://localhost:8181/cxs/rules \
 --user karaf:karaf \
 -H "Content-Type: application/json" \
--d @- &lt;&lt;'EOF'
-{
+--data-raw '{
   "metadata": {
     "id": "setContactInfo",
     "name": "Copy the received contact info to the current profile",
@@ -687,8 +1494,7 @@ tracing.</p>
       }
     }
   ]
-}
-EOF</code></pre>
+}'</code></pre>
 </div>
 </div>
 <div class="paragraph">
@@ -699,14 +1505,96 @@ course change any of the property names
 with the source of the event, such as 'mobileApp:firstName'.</p>
 </div>
 <div class="paragraph">
-<p>You could then simply send the <code>contactInfoSubmitted</code> event using a request similar to this one:</p>
+<p>Now that our rule is defined, the next step is to create a scope and a JSON Schema corresponding to the event to be submitted.</p>
+</div>
+<div class="paragraph">
+<p>We will start by creating a scope called "example" scope:</p>
 </div>
 <div class="listingblock">
 <div class="content">
-<pre class="highlight"><code>curl -X POST http://localhost:8181/eventcollector \
+<pre class="highlight"><code>curl --location --request POST 'http://localhost:8181/cxs/scopes' \
+-u 'karaf:karaf' \
+--header 'Content-Type: application/json' \
+--data-raw '{
+"itemId": "example",
+"itemType": "scope"
+}'</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>The next step consist in creating a JSON Schema to validate our event.</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="highlight"><code>curl --location --request POST 'http://localhost:8181/cxs/jsonSchema' \
+-u 'karaf:karaf' \
+--header 'Content-Type: application/json' \
+--data-raw '{
+    "$id": "https://unomi.apache.org/schemas/json/events/contactInfoSubmitted/1-0-0",
+    "$schema": "https://json-schema.org/draft/2019-09/schema",
+    "self": {
+        "vendor": "org.apache.unomi",
+        "name": "contactInfoSubmitted",
+        "format": "jsonschema",
+        "target": "events",
+        "version": "1-0-0"
+    },
+    "title": "contactInfoSubmittedEvent",
+    "type": "object",
+    "allOf": [{ "$ref": "https://unomi.apache.org/schemas/json/event/1-0-0" }],
+    "properties": {
+        "source" : {
+          "$ref" : "https://unomi.apache.org/schemas/json/item/1-0-0"
+        },
+        "target" : {
+          "$ref" : "https://unomi.apache.org/schemas/json/item/1-0-0"
+        },
+        "properties": {
+          "type": "object",
+          "properties": {
+            "firstName": {
+              "type": ["null", "string"]
+            },
+            "lastName": {
+              "type": ["null", "string"]
+            },
+            "email": {
+              "type": ["null", "string"]
+            }
+          }
+        }
+    },
+    "unevaluatedProperties": false
+}'</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>You can notice the following in the above schema:</p>
+</div>
+<div class="ulist">
+<ul>
+<li>
+<p>We are creating a schema of type "events" ("self.target" equals "events")</p>
+</li>
+<li>
+<p>The name of this schema is "contactInfoSubmitted", this MUST match the value of the "eventType" field in the event itself (below)</p>
+</li>
+<li>
+<p>To simplify our schema declaration, we&#8217;re referring to an already existing schema (<a href="https://unomi.apache.org/schemas/json/item/1-0-0" class="bare">https://unomi.apache.org/schemas/json/item/1-0-0</a>) to validate the "source" and "target" properties. Apache Unomi ships with a set of predefined JSON Schemas, detailed here: <a href="https://github.com/apache/unomi/tree/master/extensions/json-schema/services/src/main/resources/META-INF/cxs/schemas" class="bare">https://github.com/apache/unomi/tree/master/extensions/json-schema/services/src/main/resources/META-INF/cxs/schemas</a>.</p>
+</li>
+<li>
+<p><code>"unevaluatedProperties": false</code> indicates that the event should be rejected if it contains any additional metadata.</p>
+</li>
+</ul>
+</div>
+<div class="paragraph">
+<p>Finally, send the <code>contactInfoSubmitted</code> event using a request similar to this one:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="highlight"><code>curl -X POST http://localhost:8181/cxs/eventcollector \
 -H "Content-Type: application/json" \
--d @- &lt;&lt;'EOF'
-{
+--data-raw '{
     "sessionId" : "1234",
     "events":[
         {
@@ -714,29 +1602,82 @@ with the source of the event, such as 'm
             "scope": "example",
             "source":{
                 "itemType": "site",
-                "scope":"example",
+                "scope": "example",
                 "itemId": "mysite"
             },
             "target":{
-                "itemType":"form",
-                "scope":"example",
-                "itemId":"contactForm"
+                "itemType": "form",
+                "scope": "example",
+                "itemId": "contactForm"
             },
             "properties" : {
-              "firstName" : "John",
-              "lastName" : "Doe",
-              "email" : "john.doe@acme.com"
+              "firstName": "John",
+              "lastName": "Doe",
+              "email": "john.doe@acme.com"
             }
         }
     ]
-}
-EOF</code></pre>
+}'</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>The event we just submitted can be retrieved using the following request:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="highlight"><code>curl -X POST http://localhost:8181/cxs/events/search \
+--user karaf:karaf \
+-H "Content-Type: application/json" \
+--data-raw '{
+  "offset" : 0,
+  "limit" : 20,
+  "condition" : {
+    "type": "eventPropertyCondition",
+    "parameterValues" : {
+      "propertyName" : "properties.firstName",
+      "comparisonOperator" : "equals",
+      "propertyValue" : "John"
+    }
+  }
+}'</code></pre>
+</div>
+</div>
+</div>
+<div class="sect4">
+<h5 id="_troubleshooting_common_errors">Troubleshooting common errors</h5>
+<div class="paragraph">
+<p>There could be two types of common errors while customizing the above requests:
+* The schema is invalid
+* The event is invalid</p>
+</div>
+<div class="paragraph">
+<p>While first submitting the schema during its creation, Apache Unomi will validate it is syntaxically correct (JSON)
+but will not perform any further validation. Since the schema will be processed for the first time when events are submitted,
+errors might be noticeable at that time.</p>
+</div>
+<div class="paragraph">
+<p>Those errors are usually self-explanatory, such as this one pointing to an incorrect lcoation for the "firstName" keyword:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="highlight"><code>09:35:56.573 WARN [qtp1421852915-83] Unknown keyword firstName - you should define your own Meta Schema. If the keyword is irrelevant for validation, just use a NonValidationKeyword</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>If an event is invalid, the logs will contain details about the part of the event that did not validate against the schema.
+In the example below, an extra property "abcd" was added to the event:</p>
+</div>
+<div class="listingblock">
+<div class="content">

[... 4970 lines stripped ...]