You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@unomi.apache.org by sh...@apache.org on 2026/07/14 21:19:06 UTC

(unomi) branch UNOMI-963-openapi-javadoc-enrichment created (now d746396b6)

This is an automated email from the ASF dual-hosted git repository.

asf-gitbox-commits pushed a change to branch UNOMI-963-openapi-javadoc-enrichment
in repository https://gitbox.apache.org/repos/asf/unomi.git


      at d746396b6 UNOMI-963: Enrich OpenAPI docs from Javadoc across REST endpoints

This branch includes the following new commits:

     new d746396b6 UNOMI-963: Enrich OpenAPI docs from Javadoc across REST endpoints

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.



(unomi) 01/01: UNOMI-963: Enrich OpenAPI docs from Javadoc across REST endpoints

Posted by sh...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

asf-gitbox-commits pushed a commit to branch UNOMI-963-openapi-javadoc-enrichment
in repository https://gitbox.apache.org/repos/asf/unomi.git

commit d746396b61eb9f3a357c03b5ce9479740bae604a
Author: Serge Huber <sh...@apache.org>
AuthorDate: Tue Jul 14 23:18:55 2026 +0200

    UNOMI-963: Enrich OpenAPI docs from Javadoc across REST endpoints
    
    Wire openapi-enrich into rest/extensions and add @api.status / @api.example
    plus schema field docs so /cxs/openapi.json is Client/Swagger-ready (live gold
    excl. /api-docs).
---
 .../java/org/apache/unomi/api/BatchUpdate.java     |  23 +++++
 .../main/java/org/apache/unomi/api/Consent.java    |  21 +++++
 .../java/org/apache/unomi/api/ContextRequest.java  |  51 ++++++++++-
 .../java/org/apache/unomi/api/ContextResponse.java |  55 +++++++++++
 api/src/main/java/org/apache/unomi/api/Event.java  |  34 +++++++
 .../apache/unomi/api/EventsCollectorRequest.java   |  15 ++-
 api/src/main/java/org/apache/unomi/api/Item.java   |  33 +++++++
 .../main/java/org/apache/unomi/api/Metadata.java   |  40 ++++++++
 .../java/org/apache/unomi/api/PartialList.java     |  27 ++++++
 .../org/apache/unomi/api/PersonaWithSessions.java  |   6 ++
 .../apache/unomi/api/PersonalizationResult.java    |  15 ++-
 .../main/java/org/apache/unomi/api/Profile.java    |  22 +++++
 .../java/org/apache/unomi/api/ProfileAlias.java    |  16 ++++
 .../java/org/apache/unomi/api/PropertyType.java    |  47 ++++++++++
 .../main/java/org/apache/unomi/api/ServerInfo.java |   5 +
 .../main/java/org/apache/unomi/api/Session.java    |  42 +++++++++
 .../java/org/apache/unomi/api/actions/Action.java  |  13 +++
 .../org/apache/unomi/api/actions/ActionType.java   |   3 +
 .../org/apache/unomi/api/campaigns/Campaign.java   |   6 ++
 .../org/apache/unomi/api/conditions/Condition.java |  13 +++
 .../apache/unomi/api/conditions/ConditionType.java |   3 +
 .../main/java/org/apache/unomi/api/goals/Goal.java |  12 +++
 .../java/org/apache/unomi/api/query/Query.java     |  31 +++++++
 .../main/java/org/apache/unomi/api/rules/Rule.java |  28 ++++++
 .../org/apache/unomi/api/rules/RuleStatistics.java |  28 ++++++
 .../unomi/api/segments/DependentMetadata.java      |  10 ++
 .../org/apache/unomi/api/segments/Scoring.java     |   6 ++
 .../apache/unomi/api/segments/ScoringElement.java  |   6 ++
 .../org/apache/unomi/api/segments/Segment.java     |   6 ++
 .../unomi/api/services/PersonalizationService.java |  53 ++++++++++-
 .../java/org/apache/unomi/api/tenants/ApiKey.java  |  32 +++----
 .../unomi/api/tenants/ApiKeyCreationResult.java    |   8 ++
 .../java/org/apache/unomi/api/tenants/Tenant.java  |  35 ++++---
 .../unomi/api/tenants/TenantEventPurgeResult.java  |  19 +++-
 .../apache/unomi/api/tenants/TenantScopeUsage.java |   6 ++
 .../org/apache/unomi/api/tenants/TenantStatus.java |  24 ++---
 .../org/apache/unomi/api/tenants/TenantUsage.java  |  21 ++++-
 extensions/geonames/rest/pom.xml                   |  14 +++
 .../unomi/geonames/rest/GeonamesEndPoint.java      |  50 ++++++++++
 extensions/groovy-actions/rest/pom.xml             |   9 ++
 .../groovy/actions/rest/GroovyActionsEndPoint.java |  17 +++-
 extensions/healthcheck/pom.xml                     |  12 +++
 .../unomi/healthcheck/HealthCheckResponse.java     |  49 +++++++++-
 .../healthcheck/servlet/HealthCheckServlet.java    |  12 ++-
 extensions/json-schema/rest/pom.xml                |   9 ++
 .../unomi/schema/rest/JsonSchemaEndPoint.java      |  52 +++++++----
 extensions/lists-extension/rest/pom.xml            |  14 +++
 .../unomi/lists/rest/UserListServiceEndPoint.java  |  41 ++++++++-
 extensions/privacy-extension/rest/pom.xml          |  14 +++
 .../unomi/privacy/rest/PrivacyServiceEndPoint.java | 100 +++++++++++++++++++-
 extensions/router/router-rest/pom.xml              |   9 ++
 .../rest/AbstractConfigurationServiceEndpoint.java |  29 ++++--
 .../rest/ExportConfigurationServiceEndPoint.java   |  49 ++++++++--
 .../rest/ImportConfigurationServiceEndPoint.java   |  56 ++++++++++--
 extensions/salesforce-connector/rest/pom.xml       |   9 ++
 .../org/apache/unomi/sfdc/rest/SFDCEndPoint.java   |  17 ++++
 pom.xml                                            |  27 ++++++
 rest/pom.xml                                       |  48 ++++++++++
 .../rest/endpoints/CampaignsServiceEndPoint.java   |  31 +++++++
 .../unomi/rest/endpoints/ClientEndpoint.java       |  17 +++-
 .../rest/endpoints/ClusterServiceEndPoint.java     |  13 ++-
 .../unomi/rest/endpoints/ContextJsonEndpoint.java  |  90 ++++++++++++------
 .../rest/endpoints/DefinitionsServiceEndPoint.java |  86 +++++++++++++++---
 .../unomi/rest/endpoints/EventServiceEndpoint.java |  13 ++-
 .../rest/endpoints/EventsCollectorEndpoint.java    |  31 +++++--
 .../unomi/rest/endpoints/GoalsServiceEndPoint.java |  23 +++++
 .../unomi/rest/endpoints/PatchServiceEndPoint.java |   8 +-
 .../rest/endpoints/ProfileServiceEndPoint.java     | 101 ++++++++++++++++++++-
 .../unomi/rest/endpoints/QueryServiceEndPoint.java |  16 +++-
 .../unomi/rest/endpoints/RulesServiceEndPoint.java |  31 +++++++
 .../unomi/rest/endpoints/ScopeServiceEndPoint.java |  11 ++-
 .../rest/endpoints/ScoringServiceEndPoint.java     |  38 ++++++--
 .../rest/endpoints/SegmentServiceEndPoint.java     |  52 ++++++++---
 .../apache/unomi/rest/endpoints/TestEndPoint.java  |   6 +-
 .../rest/endpoints/UserListServiceEndPoint.java    |   2 +
 .../unomi/rest/models/EventCollectorResponse.java  |  19 +---
 .../apache/unomi/rest/scheduler/TaskEndpoint.java  |  55 ++++++-----
 .../org/apache/unomi/rest/server/RestServer.java   |   7 +-
 .../apache/unomi/rest/tenants/TenantEndpoint.java  |  57 ++++++++++--
 .../apache/unomi/rest/tenants/TenantRequest.java   |  16 +++-
 .../org/apache/unomi/tracing/api/TraceNode.java    |  32 +++++++
 81 files changed, 1960 insertions(+), 247 deletions(-)

diff --git a/api/src/main/java/org/apache/unomi/api/BatchUpdate.java b/api/src/main/java/org/apache/unomi/api/BatchUpdate.java
index a68068ee3..3bef97927 100644
--- a/api/src/main/java/org/apache/unomi/api/BatchUpdate.java
+++ b/api/src/main/java/org/apache/unomi/api/BatchUpdate.java
@@ -26,11 +26,34 @@ import org.apache.unomi.api.conditions.Condition;
  * applies a {@link PropertyMergeStrategyType} when merging complex fields.
  */
 public class BatchUpdate {
+    /**
+     * Property to update, as an Apache Commons BeanUtils expression (for example {@code properties.email}).
+     * @api.example properties.email
+     */
     private String propertyName;
+    /**
+     * New value to assign to the property.
+     * @api.example updated@example.com
+     */
     private Object propertyValue;
+    /**
+     * Condition that items must match to be updated.
+     */
     private Condition condition;
+    /**
+     * Optional property merge strategy name when updating complex fields.
+     * @api.example defaultPropertyMergeStrategy
+     */
     private String strategy;
+    /**
+     * Scroll keep-alive window while iterating matches.
+     * @api.example 10m
+     */
     private String scrollTimeValidity = "10m";
+    /**
+     * Number of matching items to process per scroll batch.
+     * @api.example 1000
+     */
     private int scrollBatchSize = 1000;
 
     /**
diff --git a/api/src/main/java/org/apache/unomi/api/Consent.java b/api/src/main/java/org/apache/unomi/api/Consent.java
index 966c9a53d..5b16a4f7e 100644
--- a/api/src/main/java/org/apache/unomi/api/Consent.java
+++ b/api/src/main/java/org/apache/unomi/api/Consent.java
@@ -31,10 +31,30 @@ import java.util.Map;
  */
 public class Consent implements Serializable {
 
+    /**
+     * Scope this consent applies to (often a site id).
+     * @api.example mysite
+     */
     private String scope;
+    /**
+     * Consent type id defined outside Unomi (newsletter, tracking, ...).
+     * @api.example newsletter
+     */
     private String typeIdentifier; // type identifiers are defined and managed externally of Apache Unomi
+    /**
+     * Consent status: {@code GRANTED}, {@code DENIED}, or {@code REVOKED}.
+     * @api.example GRANTED
+     */
     private ConsentStatus status;
+    /**
+     * When this status was recorded (ISO-8601 in JSON).
+     * @api.example 2024-01-15T09:00:00.000Z
+     */
     private Date statusDate;
+    /**
+     * Optional automatic revoke date; omitted when the consent does not expire.
+     * @api.example 2025-01-15T09:00:00.000Z
+     */
     private Date revokeDate;
 
     /**
@@ -124,6 +144,7 @@ public class Consent implements Serializable {
      * Consent type identifier.
      *
      * @return the type identifier
+     * @api.example newsletter
      */
     public String getTypeIdentifier() {
         return typeIdentifier;
diff --git a/api/src/main/java/org/apache/unomi/api/ContextRequest.java b/api/src/main/java/org/apache/unomi/api/ContextRequest.java
index 6a81561e8..d302befc6 100644
--- a/api/src/main/java/org/apache/unomi/api/ContextRequest.java
+++ b/api/src/main/java/org/apache/unomi/api/ContextRequest.java
@@ -50,29 +50,78 @@ import java.util.Map;
 
 public class ContextRequest {
 
+    /**
+     * Request source item (typically a page/site {@code Item} with {@code scope}). Used to resolve tracked conditions.
+     */
     private Item source;
+    /**
+     * When {@code true}, response includes {@code profileSegments}.
+     * @api.example true
+     */
     private boolean requireSegments;
+    /**
+     * Profile property names to return, or {@code ["*"]} for public (non-concealed) properties.
+     * @api.example ["firstName","email"]
+     */
     private List<String> requiredProfileProperties;
+    /**
+     * Session property names to return, or {@code ["*"]} for all session properties.
+     * @api.example ["*"]
+     */
     private List<String> requiredSessionProperties;
+    /**
+     * When {@code true}, response includes {@code profileScores}.
+     * @api.example false
+     */
     private boolean requireScores;
+    /**
+     * Client-generated events to process before building the response (view, click, login, …).
+     */
     private List<Event> events;
+    /**
+     * Content personalization filters to evaluate against the (possibly overridden) profile/session.
+     */
     private List<PersonalizationService.PersonalizedContent> filters;
+    /**
+     * Personalization requests (ordered content lists) to resolve for this profile/session.
+     */
     private List<PersonalizationService.PersonalizationRequest> personalizations;
 
     // the following overrides make it possible to override temporarily the current profile segments, properties or
     // even session properties. This is useful for building UIs to temporarily override one of these parameters to
     // test different filter results.
+    /**
+     * Temporary profile overrides for persona/preview UIs (segments, scores, properties). Applied only when the
+     * active profile is a {@link Persona}.
+     */
     private Profile profileOverrides;
+    /**
+     * Temporary session property overrides for persona/preview UIs.
+     * @api.example {"utm_source":"newsletter"}
+     */
     private Map<String, Object> sessionPropertiesOverrides;
 
+    /**
+     * Session identifier for this request (preferred over {@code sessionId} query param).
+     * @api.example session-1
+     */
     private String sessionId;
 
+    /**
+     * Profile identifier hint for this request.
+     * @api.example profile-1
+     */
     private String profileId;
 
+    /**
+     * Optional client / channel identifier.
+     * @api.example web-tracker
+     */
     private String clientId;
 
     /**
-     * The public API key for tenant authentication.
+     * Public API key for tenant authentication (multi-tenant deployments).
+     * @api.example pk_live_example
      */
     private String publicApiKey;
 
diff --git a/api/src/main/java/org/apache/unomi/api/ContextResponse.java b/api/src/main/java/org/apache/unomi/api/ContextResponse.java
index b6e57c341..7a52dbf88 100644
--- a/api/src/main/java/org/apache/unomi/api/ContextResponse.java
+++ b/api/src/main/java/org/apache/unomi/api/ContextResponse.java
@@ -34,32 +34,87 @@ public class ContextResponse implements Serializable {
 
     private static final long serialVersionUID = -5638595408986826332L;
 
+    /**
+     * Resolved profile id for this evaluation.
+     * @api.example profile-1
+     */
     private String profileId;
 
+    /**
+     * Resolved session id for this evaluation (may be omitted when no session exists).
+     * @api.example session-1
+     */
     private String sessionId;
 
+    /**
+     * Profile properties subset requested via {@link ContextRequest#getRequiredProfileProperties()}.
+     * @api.example {"firstName":"Ada"}
+     */
     private Map<String, Object> profileProperties;
 
+    /**
+     * Session properties subset requested via {@link ContextRequest#getRequiredSessionProperties()}.
+     * @api.example {"utm_source":"newsletter"}
+     */
     private Map<String, Object> sessionProperties;
 
+    /**
+     * Segment ids for the profile when {@link ContextRequest#isRequireSegments()} was true.
+     * @api.example ["vip","returning"]
+     */
     private Set<String> profileSegments;
 
+    /**
+     * Scoring plan id → score when {@link ContextRequest#isRequireScores()} was true.
+     * @api.example {"engagement":12}
+     */
     private Map<String,Integer> profileScores;
 
+    /**
+     * Filter id → whether the profile matched that content filter.
+     * @api.example {"hero-banner":true}
+     */
     private Map<String, Boolean> filteringResults;
 
+    /**
+     * Number of events from the request that were processed.
+     * @api.example 1
+     */
     private int processedEvents;
 
+    /**
+     * Legacy personalization id → selected content ids.
+     * Prefer {@link #personalizationResults} since 2.1.0.
+     * @api.example {"hero":["variant-a"]}
+     */
     private Map<String, List<String>> personalizations;
 
+    /**
+     * Personalization id → resolution result (content ids, scores, filters).
+     * Prefer this over the legacy {@link #personalizations} map.
+     */
     private Map<String, PersonalizationResult> personalizationResults;
 
+    /**
+     * Tracked conditions clients should watch for (for example form field mapping rules).
+     */
     private Set<Condition> trackedConditions;
 
+    /**
+     * {@code true} when privacy requires anonymous browsing for this profile.
+     * @api.example false
+     */
     private boolean anonymousBrowsing;
 
+    /**
+     * Consent map for the profile, keyed by consent identifier.
+     * @api.example {"newsletter":{"scope":"mysite","typeIdentifier":"newsletter","status":"GRANTED"}}
+     */
     private Map<String, Consent> consents = new LinkedHashMap<>();
 
+    /**
+     * Present only when {@code explain=true} and the caller is an administrator / tenant administrator.
+     */
     private TraceNode requestTracing;
 
     /**
diff --git a/api/src/main/java/org/apache/unomi/api/Event.java b/api/src/main/java/org/apache/unomi/api/Event.java
index 988e11cda..8c66a35e6 100644
--- a/api/src/main/java/org/apache/unomi/api/Event.java
+++ b/api/src/main/java/org/apache/unomi/api/Event.java
@@ -53,20 +53,54 @@ public class Event extends Item implements TimestampedItem {
     public static final String CLIENT_ID_ATTRIBUTE = "client_id";
 
     private static final long serialVersionUID = -1096874942838593575L;
+    /**
+     * Event verb / type id (for example {@code view}, {@code login}, {@code modifyProperties}).
+     * @api.example view
+     */
     private String eventType;
+    /**
+     * Session this event belongs to (optional if the collector request sets a default).
+     * @api.example session-1
+     */
     private String sessionId = null;
+    /**
+     * Profile this event belongs to (optional if the collector/context request sets a default).
+     * @api.example profile-1
+     */
     private String profileId = null;
+    /**
+     * Event time; defaults to receive time when omitted (ISO-8601 in JSON).
+     * @api.example 2024-06-15T10:30:00.000Z
+     */
     private Date timeStamp;
+    /**
+     * Event-specific properties (URL, product id, form fields, …). Free-form map.
+     * @api.example {"pageUrl":"https://example.com/"}
+     */
     private Map<String, Object> properties;
+    /**
+     * Flattened copy of nested properties used by some query paths.
+     * @api.example {"pageUrl":"https://example.com/"}
+     */
     private Map<String, Object> flattenedProperties;
 
     private transient Profile profile;
     private transient Session session;
     private transient List<ActionPostExecutor> actionPostExecutors;
 
+    /**
+     * Event source (subject), often a page {@link Item} with {@code scope} and {@code itemId}.
+     */
     private Item source;
+    /**
+     * Event target (object), when applicable.
+     */
     private Item target;
 
+    /**
+     * When {@code false}, the event is evaluated by rules but not persisted.
+     * @api.example true
+     */
     private boolean persistent = true;
 
     private transient Map<String, Object> attributes;
diff --git a/api/src/main/java/org/apache/unomi/api/EventsCollectorRequest.java b/api/src/main/java/org/apache/unomi/api/EventsCollectorRequest.java
index 49ff69965..6e3f70cc8 100644
--- a/api/src/main/java/org/apache/unomi/api/EventsCollectorRequest.java
+++ b/api/src/main/java/org/apache/unomi/api/EventsCollectorRequest.java
@@ -27,14 +27,27 @@ import java.util.List;
  */
 public class EventsCollectorRequest {
 
+    /**
+     * Events to collect and evaluate. At least one event is expected for a useful call.
+     * @api.example [{"eventType":"view","scope":"mysite","source":{"itemType":"page","scope":"mysite","itemId":"/home"}}]
+     */
     private List<Event> events;
 
+    /**
+     * Default session id when individual events do not specify one (prefer body over query string).
+     * @api.example session-1
+     */
     private String sessionId;
 
+    /**
+     * Default profile id when individual events do not specify one.
+     * @api.example profile-1
+     */
     private String profileId;
 
     /**
-     * The public API key for tenant authentication.
+     * Public API key for tenant authentication (multi-tenant deployments).
+     * @api.example pk_live_example
      */
     private String publicApiKey;
 
diff --git a/api/src/main/java/org/apache/unomi/api/Item.java b/api/src/main/java/org/apache/unomi/api/Item.java
index b8e63d4f2..7526e11d3 100644
--- a/api/src/main/java/org/apache/unomi/api/Item.java
+++ b/api/src/main/java/org/apache/unomi/api/Item.java
@@ -77,35 +77,68 @@ public abstract class Item implements Serializable, YamlConvertible {
     /**
      * Unique id used when this item is persisted or referenced.
      * Must be unique among items of the same {@link #itemType}.
+     * @api.example profile-1
      */
     protected String itemId;
     /**
      * Persistence type string for this item.
      * Subclasses must define a public {@code ITEM_TYPE} constant with this value.
+     * @api.example profile
      */
     protected String itemType;
     /**
      * Scope that groups related items (often one analyzed site).
      * Used by clients to filter data returned from the context server.
+     * @api.example mysite
      */
     protected String scope;
     /**
      * Optimistic-locking version, incremented when the item is updated.
+     * @api.example 3
      */
     protected Long version;
     /**
      * Server-managed metadata keyed by string.
      * Stores values that are not part of the item's core properties.
+     * @api.example {"origin":"import"}
      */
     protected Map<String, Object> systemMetadata = new HashMap<>();
+    /**
+     * Tenant that owns this item.
+     * @api.example acme
+     */
     private String tenantId;
 
     // Audit metadata fields
+    /**
+     * User or system that created this item.
+     * @api.example karaf
+     */
     private String createdBy;
+    /**
+     * User or system that last modified this item.
+     * @api.example karaf
+     */
     private String lastModifiedBy;
+    /**
+     * When this item was created (ISO-8601 in JSON).
+     * @api.example 2024-06-15T10:30:00.000Z
+     */
     private Date creationDate;
+    /**
+     * When this item was last modified (ISO-8601 in JSON).
+     * @api.example 2024-06-15T11:00:00.000Z
+     */
     private Date lastModificationDate;
+    /**
+     * Cluster node that originated this item.
+     * @api.example node-1
+     */
     private String sourceInstanceId;
+    /**
+     * When this item was last synchronized from another node (ISO-8601 in JSON).
+     * @api.example 2024-06-15T11:05:00.000Z
+     */
     private Date lastSyncDate;
 
     /**
diff --git a/api/src/main/java/org/apache/unomi/api/Metadata.java b/api/src/main/java/org/apache/unomi/api/Metadata.java
index ec643e2d8..ab1d14f41 100644
--- a/api/src/main/java/org/apache/unomi/api/Metadata.java
+++ b/api/src/main/java/org/apache/unomi/api/Metadata.java
@@ -43,15 +43,55 @@ public class Metadata implements Comparable<Metadata>, Serializable, YamlConvert
      * Default scope, gathers default entities and can also be used to share entities across scopes.
      */
     public static final String SYSTEM_SCOPE = "systemscope";
+    /**
+     * Stable identifier of this metadata item (often equals itemId of the owning entity).
+     * @api.example vip
+     */
     private String id;
+    /**
+     * Display name.
+     * @api.example VIP customers
+     */
     private String name;
+    /**
+     * Human-readable description.
+     * @api.example Customers with high engagement score
+     */
     private String description;
+    /**
+     * Scope that owns this metadata.
+     * @api.example mysite
+     */
     private String scope;
+    /**
+     * User-visible tags.
+     * @api.example ["marketing"]
+     */
     private Set<String> tags = new LinkedHashSet<>();
+    /**
+     * System tags used by Unomi internals and plugins.
+     * @api.example ["profileProperties"]
+     */
     private Set<String> systemTags = new LinkedHashSet<>();
+    /**
+     * Whether this item is enabled.
+     * @api.example true
+     */
     private boolean enabled = true;
+    /**
+     * {@code true} when required plugins are not installed.
+     * @api.example false
+     */
     private boolean missingPlugins = false;
+    /**
+     * Whether this item is hidden from default UIs.
+     * @api.example false
+     */
     private boolean hidden = false;
+    /**
+     * Whether this item is read-only.
+     * @api.example false
+     */
     private boolean readOnly = false;
 
     /**
diff --git a/api/src/main/java/org/apache/unomi/api/PartialList.java b/api/src/main/java/org/apache/unomi/api/PartialList.java
index f6fe43a44..f0d3f615c 100644
--- a/api/src/main/java/org/apache/unomi/api/PartialList.java
+++ b/api/src/main/java/org/apache/unomi/api/PartialList.java
@@ -32,12 +32,39 @@ import java.util.List;
 public class PartialList<T> implements Serializable {
 
     private static final long serialVersionUID = 2661946814840468260L;
+    /**
+     * Elements in the current page.
+     */
     private List<T> list;
+    /**
+     * Zero-based index of the first element of this page in the full result set.
+     * @api.example 0
+     */
     private long offset;
+    /**
+     * Number of elements in this page.
+     * @api.example 20
+     */
     private long pageSize;
+    /**
+     * Total number of matching elements (exact or lower bound; see {@link #totalSizeRelation}).
+     * @api.example 120
+     */
     private long totalSize;
+    /**
+     * Whether {@link #totalSize} is exact ({@code EQUAL}) or a lower bound ({@code GREATER_THAN_OR_EQUAL_TO}).
+     * @api.example EQUAL
+     */
     private Relation totalSizeRelation;
+    /**
+     * Scroll identifier for continuing a deep scroll query, when applicable.
+     * @api.example DXF1ZXJ5QW5kRmV0Y2gBAAAAAAAAAD4WYmRUMAkwZGY=
+     */
     private String scrollIdentifier = null;
+    /**
+     * Scroll keep-alive window associated with {@link #scrollIdentifier}.
+     * @api.example 10m
+     */
     private String scrollTimeValidity = null;
 
     /**
diff --git a/api/src/main/java/org/apache/unomi/api/PersonaWithSessions.java b/api/src/main/java/org/apache/unomi/api/PersonaWithSessions.java
index 0c6142c89..c9624b163 100644
--- a/api/src/main/java/org/apache/unomi/api/PersonaWithSessions.java
+++ b/api/src/main/java/org/apache/unomi/api/PersonaWithSessions.java
@@ -27,8 +27,14 @@ import java.util.List;
  * the sessions needed to simulate or inspect that persona.
  */
 public class PersonaWithSessions implements Serializable {
+    /**
+     * The persona being simulated or inspected.
+     */
     private Persona persona;
 
+    /**
+     * Sessions associated with the persona.
+     */
     private List<PersonaSession> sessions;
 
     /**
diff --git a/api/src/main/java/org/apache/unomi/api/PersonalizationResult.java b/api/src/main/java/org/apache/unomi/api/PersonalizationResult.java
index 0dab6d134..5134cd362 100644
--- a/api/src/main/java/org/apache/unomi/api/PersonalizationResult.java
+++ b/api/src/main/java/org/apache/unomi/api/PersonalizationResult.java
@@ -38,13 +38,22 @@ public class PersonalizationResult implements Serializable  {
      */
     public final static String ADDITIONAL_RESULT_INFO_IN_CONTROL_GROUP = "inControlGroup";
 
-    /** Matching content identifiers for the resolved personalization. */
+    /**
+     * Matching content identifiers for the resolved personalization (ordered by strategy).
+     * @api.example ["hero-a","hero-b"]
+     */
     List<String> contentIds;
 
-    /** Extra key/value metadata returned to the client (for example control group flags). */
+    /**
+     * Extra key/value metadata returned to the client. Well-known key:
+     * {@link #ADDITIONAL_RESULT_INFO_IN_CONTROL_GROUP} ({@code inControlGroup}) → boolean.
+     * @api.example {"inControlGroup":false}
+     */
     Map<String, Object> additionalResultInfos = new HashMap<>();
 
-    /** Internal change flags when resolution updated the profile or session. */
+    /**
+     * Internal change flags when resolution updated the profile or session (not serialized on the wire).
+     */
     int changeType = EventService.NO_CHANGE;
 
     /**
diff --git a/api/src/main/java/org/apache/unomi/api/Profile.java b/api/src/main/java/org/apache/unomi/api/Profile.java
index 5a8ee35d3..02bf24e21 100644
--- a/api/src/main/java/org/apache/unomi/api/Profile.java
+++ b/api/src/main/java/org/apache/unomi/api/Profile.java
@@ -47,20 +47,42 @@ public class Profile extends Item implements SystemPropertiesItem {
      */
     public static final String ITEM_TYPE = "profile";
     private static final long serialVersionUID = -7409439322939712238L;
+    /**
+     * All user-visible profile properties.
+     * @api.example {"firstName":"Ada","email":"ada@example.com"}
+     */
     private Map<String, Object> properties = new HashMap<>();
 
+    /**
+     * Internal properties used by implementations and not usually returned to clients.
+     */
     private Map<String, Object> systemProperties = new HashMap<>();
 
+    /**
+     * Segment identifiers matched by this profile.
+     * @api.example ["vip","returning"]
+     */
     private Set<String> segments = new HashSet<>();
 
+    /**
+     * Scoring plan id → numeric score for this profile.
+     * @api.example {"engagement":12}
+     */
     private Map<String, Integer> scores;
 
     /**
+     * Legacy merge target profile id; unused since 2.0.0 (profile aliases replaced merges).
+     *
      * @deprecated since 2.0.0 merge mechanism is now based on profile aliases, and this property is not used anymore
+     * @api.example profile-merged-target
      */
     @Deprecated
     private String mergedWith;
 
+    /**
+     * Consent map for this profile, keyed by consent identifier.
+     * @api.example {"newsletter":{"scope":"mysite","typeIdentifier":"newsletter","status":"GRANTED"}}
+     */
     private Map<String, Consent> consents = new LinkedHashMap<>();
 
     /**
diff --git a/api/src/main/java/org/apache/unomi/api/ProfileAlias.java b/api/src/main/java/org/apache/unomi/api/ProfileAlias.java
index cdfe734b1..6bcb7a797 100644
--- a/api/src/main/java/org/apache/unomi/api/ProfileAlias.java
+++ b/api/src/main/java/org/apache/unomi/api/ProfileAlias.java
@@ -32,12 +32,28 @@ public class ProfileAlias extends Item {
      */
     public static final String ITEM_TYPE = "profileAlias";
 
+    /**
+     * Canonical profile id linked by this alias.
+     * @api.example profile-1
+     */
     private String profileID;
 
+    /**
+     * Client that owns / created this alias link.
+     * @api.example web-tracker
+     */
     private String clientID;
 
+    /**
+     * When this alias was created (ISO-8601 in JSON).
+     * @api.example 2024-06-15T10:00:00.000Z
+     */
     private Date creationTime;
 
+    /**
+     * When this alias was last modified (ISO-8601 in JSON).
+     * @api.example 2024-06-15T11:00:00.000Z
+     */
     private Date modifiedTime;
 
     /**
diff --git a/api/src/main/java/org/apache/unomi/api/PropertyType.java b/api/src/main/java/org/apache/unomi/api/PropertyType.java
index 25b3117d8..fb04f0cf4 100644
--- a/api/src/main/java/org/apache/unomi/api/PropertyType.java
+++ b/api/src/main/java/org/apache/unomi/api/PropertyType.java
@@ -37,18 +37,65 @@ public class PropertyType extends MetadataItem {
      */
     public static final String ITEM_TYPE = "propertyType";
 
+    /**
+     * Item type this property applies to (for example {@code profiles}).
+     * @api.example profiles
+     */
     private String target;
+    /**
+     * Value type identifier (for example {@code string}, {@code email}, {@code integer}).
+     * @api.example email
+     */
     private String valueTypeId;
+    /**
+     * Resolved value type definition (optional; usually derived from {@link #valueTypeId}).
+     */
     private ValueType valueType;
+    /**
+     * Optional default value for new items.
+     * @api.example unknown@example.com
+     */
     private String defaultValue;
+    /**
+     * Optional allowed date ranges for date-typed properties.
+     */
     private List<DateRange> dateRanges = new ArrayList<>();
+    /**
+     * Optional allowed numeric ranges for numeric properties.
+     */
     private List<NumericRange> numericRanges = new ArrayList<>();
+    /**
+     * Optional allowed IP ranges for IP-typed properties.
+     */
     private List<IpRange> ipRanges = new ArrayList<>();
+    /**
+     * External property names automatically mapped onto this property type.
+     * @api.example ["mail","e-mail"]
+     */
     private Set<String> automaticMappingsFrom = new HashSet<>();
+    /**
+     * Display/order rank among sibling property types.
+     * @api.example 10.0
+     */
     private Double rank;
+    /**
+     * Strategy used when merging this property across profiles.
+     * @api.example defaultPropertyMergeStrategy
+     */
     private String mergeStrategy;
+    /**
+     * Whether the property accepts multiple values.
+     * @api.example false
+     */
     private Boolean multivalued;
+    /**
+     * When {@code true}, the property is protected / concealed from public APIs.
+     * @api.example false
+     */
     private Boolean protekted;
+    /**
+     * Nested child property types for structured properties.
+     */
     private Set<PropertyType> childPropertyTypes = new LinkedHashSet<>();
 
     /**
diff --git a/api/src/main/java/org/apache/unomi/api/ServerInfo.java b/api/src/main/java/org/apache/unomi/api/ServerInfo.java
index dd0a46d00..b2ce88d87 100644
--- a/api/src/main/java/org/apache/unomi/api/ServerInfo.java
+++ b/api/src/main/java/org/apache/unomi/api/ServerInfo.java
@@ -30,8 +30,11 @@ import java.util.Map;
  */
 public class ServerInfo {
 
+    /** @api.example unomi */
     private String serverIdentifier;
+    /** @api.example 3.1.0-SNAPSHOT */
     private String serverVersion;
+    /** @api.example 1 */
     private String serverBuildNumber;
     private Date serverBuildDate;
     private String serverTimestamp;
@@ -52,6 +55,7 @@ public class ServerInfo {
      * Unique identifier for this server instance.
      *
      * @return server identifier
+     * @api.example unomi
      */
     public String getServerIdentifier() {
         return serverIdentifier;
@@ -70,6 +74,7 @@ public class ServerInfo {
      * Running Unomi version string.
      *
      * @return server version
+     * @api.example 3.1.0-SNAPSHOT
      */
     public String getServerVersion() {
         return serverVersion;
diff --git a/api/src/main/java/org/apache/unomi/api/Session.java b/api/src/main/java/org/apache/unomi/api/Session.java
index ad986b931..f05a1e5b7 100644
--- a/api/src/main/java/org/apache/unomi/api/Session.java
+++ b/api/src/main/java/org/apache/unomi/api/Session.java
@@ -36,25 +36,67 @@ public class Session extends Item implements TimestampedItem, SystemPropertiesIt
      */
     public static final String ITEM_TYPE = "session";
     private static final long serialVersionUID = 4628640198281687336L;
+    /**
+     * Identifier of the profile linked to this session.
+     * @api.example profile-1
+     */
     private String profileId;
 
+    /**
+     * Profile associated with this session.
+     */
     private Profile profile;
 
+    /**
+     * All user-visible session properties.
+     * @api.example {"utm_source":"newsletter"}
+     */
     private Map<String, Object> properties = new HashMap<>();
 
+    /**
+     * Internal properties used by implementations and not usually returned to clients.
+     */
     private Map<String, Object> systemProperties = new HashMap<>();
 
+    /**
+     * When this session was created (ISO-8601 in JSON).
+     * @api.example 2024-06-15T10:00:00.000Z
+     */
     private Date timeStamp;
 
+    /**
+     * Scope this session belongs to.
+     * @api.example mysite
+     */
     private String scope;
 
+    /**
+     * Timestamp of the most recent event in this session (ISO-8601 in JSON).
+     * @api.example 2024-06-15T10:30:00.000Z
+     */
     private Date lastEventDate;
 
+    /**
+     * Number of events recorded in this session.
+     * @api.example 4
+     */
     private int size = 0;
 
+    /**
+     * Elapsed time between session start and last event, in milliseconds.
+     * @api.example 1800000
+     */
     private int duration = 0;
 
+    /**
+     * Event types that triggered session creation.
+     * @api.example ["view"]
+     */
     private List<String> originEventTypes = new ArrayList<>();
+    /**
+     * Event ids that triggered session creation.
+     * @api.example ["evt-1"]
+     */
     private List<String> originEventIds = new ArrayList<>();
 
     /**
diff --git a/api/src/main/java/org/apache/unomi/api/actions/Action.java b/api/src/main/java/org/apache/unomi/api/actions/Action.java
index 7d10fe5d7..a7e32bdf1 100644
--- a/api/src/main/java/org/apache/unomi/api/actions/Action.java
+++ b/api/src/main/java/org/apache/unomi/api/actions/Action.java
@@ -38,7 +38,18 @@ import static org.apache.unomi.api.utils.YamlUtils.toYamlValue;
  */
 public class Action implements Serializable, YamlConvertible {
     private ActionType actionType;
+    /**
+     * Identifier of the action type to execute.
+     * In JSON bodies this id is usually serialized as {@code type}
+     * (for example {@code setPropertyAction}, {@code allEventToProfilePropertiesAction}).
+     * @api.example setPropertyAction
+     */
     private String actionTypeId;
+    /**
+     * Parameter values for the action (names depend on the action type).
+     * For {@code setPropertyAction}: {@code setPropertyName}, then one value param ({@code setPropertyValue} string, {@code setPropertyValueBoolean}, {@code setPropertyValueInteger}, or a helper such as {@code setPropertyValueCurrentDate}), optional {@code storeInSession}.
+     * @api.example {"setPropertyName":"properties.isPremium","setPropertyValueBoolean":true,"storeInSession":false}
+     */
     private Map<String, Object> parameterValues = new HashMap<>();
 
     /**
@@ -80,6 +91,7 @@ public class Action implements Serializable, YamlConvertible {
      * Identifier of the associated action type.
      *
      * @return the identifier of the associated action type
+     * @api.example setPropertyAction
      */
     @XmlElement(name = "type")
     public String getActionTypeId() {
@@ -99,6 +111,7 @@ public class Action implements Serializable, YamlConvertible {
      * Parameter values keyed by parameter name.
      *
      * @return a Map of parameter name - associated value pairs
+     * @api.example {"setPropertyName":"properties.isPremium","setPropertyValueBoolean":true,"storeInSession":false}
      */
     public Map<String, Object> getParameterValues() {
         return parameterValues;
diff --git a/api/src/main/java/org/apache/unomi/api/actions/ActionType.java b/api/src/main/java/org/apache/unomi/api/actions/ActionType.java
index 501dafd97..0e0e89dca 100644
--- a/api/src/main/java/org/apache/unomi/api/actions/ActionType.java
+++ b/api/src/main/java/org/apache/unomi/api/actions/ActionType.java
@@ -42,6 +42,9 @@ public class ActionType extends MetadataItem implements PluginType, YamlConverti
     private static final long serialVersionUID = -3522958600710010935L;
     private String actionExecutor;
     private List<Parameter> parameters = new ArrayList<Parameter>();
+    /**
+     * Bundle/plugin id that contributed this action type definition.
+     */
     private long pluginId;
 
     /**
diff --git a/api/src/main/java/org/apache/unomi/api/campaigns/Campaign.java b/api/src/main/java/org/apache/unomi/api/campaigns/Campaign.java
index f28789738..438bcc23f 100644
--- a/api/src/main/java/org/apache/unomi/api/campaigns/Campaign.java
+++ b/api/src/main/java/org/apache/unomi/api/campaigns/Campaign.java
@@ -42,6 +42,10 @@ public class Campaign extends MetadataItem {
 
     private Date endDate;
 
+    /**
+     * Condition a profile must satisfy to enter the campaign (JSON wire field {@code type} + {@code parameterValues}).
+     * @api.example {"type":"eventTypeCondition","parameterValues":{"eventTypeId":"view"}}
+     */
     private Condition entryCondition;
 
     private Double cost;
@@ -105,8 +109,10 @@ public class Campaign extends MetadataItem {
 
     /**
      * Condition a profile must satisfy to enter the campaign.
+     * In JSON the condition type id is usually the field {@code type} (not {@code conditionTypeId}).
      *
      * @return the entry condition
+     * @api.example {"type":"eventTypeCondition","parameterValues":{"eventTypeId":"view"}}
      */
     public Condition getEntryCondition() {
         return entryCondition;
diff --git a/api/src/main/java/org/apache/unomi/api/conditions/Condition.java b/api/src/main/java/org/apache/unomi/api/conditions/Condition.java
index fd7655286..b30ed6b42 100644
--- a/api/src/main/java/org/apache/unomi/api/conditions/Condition.java
+++ b/api/src/main/java/org/apache/unomi/api/conditions/Condition.java
@@ -46,7 +46,20 @@ public class Condition implements Serializable, YamlConvertible {
     private static final long serialVersionUID = 7584522402785053206L;
 
     ConditionType conditionType;
+    /**
+     * Identifier of the condition type backing this expression.
+     * In JSON request/response bodies this id is usually serialized as {@code type}
+     * (for example {@code eventTypeCondition}, {@code profilePropertyCondition}, {@code booleanCondition}).
+     * @api.example eventTypeCondition
+     */
     String conditionTypeId;
+    /**
+     * Parameter values supplied for this condition instance (names depend on the condition type).
+     * Examples: {@code eventTypeCondition} uses {@code eventTypeId};
+     * {@code profilePropertyCondition} uses {@code propertyName}/{@code comparisonOperator}/{@code propertyValue*};
+     * {@code booleanCondition} uses {@code operator} plus nested {@code subConditions}.
+     * @api.example {"eventTypeId":"view"}
+     */
     Map<String, Object> parameterValues = new HashMap<>();
 
     /**
diff --git a/api/src/main/java/org/apache/unomi/api/conditions/ConditionType.java b/api/src/main/java/org/apache/unomi/api/conditions/ConditionType.java
index 4adadf8e6..aab662988 100644
--- a/api/src/main/java/org/apache/unomi/api/conditions/ConditionType.java
+++ b/api/src/main/java/org/apache/unomi/api/conditions/ConditionType.java
@@ -46,6 +46,9 @@ public class ConditionType extends MetadataItem implements PluginType, YamlConve
     private String queryBuilder;
     private Condition parentCondition;
     private List<Parameter> parameters = new ArrayList<Parameter>();
+    /**
+     * Bundle/plugin id that contributed this condition type definition.
+     */
     private long pluginId;
 
     /**
diff --git a/api/src/main/java/org/apache/unomi/api/goals/Goal.java b/api/src/main/java/org/apache/unomi/api/goals/Goal.java
index 673191015..1b6862763 100644
--- a/api/src/main/java/org/apache/unomi/api/goals/Goal.java
+++ b/api/src/main/java/org/apache/unomi/api/goals/Goal.java
@@ -45,8 +45,16 @@ public class Goal extends MetadataItem implements YamlConvertible {
     /** The constant string used to identify this object as a goal item type. */
     public static final String ITEM_TYPE = "goal";
     private static final long serialVersionUID = 6131648013470949983L;
+    /**
+     * Optional start condition for funnel-style goals (JSON wire field {@code type} + {@code parameterValues}).
+     * @api.example {"type":"eventTypeCondition","parameterValues":{"eventTypeId":"view"}}
+     */
     private Condition startEvent;
 
+    /**
+     * Condition that marks the goal as accomplished (JSON wire field {@code type} + {@code parameterValues}).
+     * @api.example {"type":"eventTypeCondition","parameterValues":{"eventTypeId":"purchase"}}
+     */
     private Condition targetEvent;
 
     private String campaignId;
@@ -69,8 +77,10 @@ public class Goal extends MetadataItem implements YamlConvertible {
     /**
      * Optional start condition for funnel-style goals.
      * When set, goal tracking begins only after this condition is met.
+     * In JSON the condition type id is usually the field {@code type} (not {@code conditionTypeId}).
      *
      * @return the start condition, or {@code null} if none is configured
+     * @api.example {"type":"eventTypeCondition","parameterValues":{"eventTypeId":"view"}}
      */
     public Condition getStartEvent() {
         return startEvent;
@@ -87,8 +97,10 @@ public class Goal extends MetadataItem implements YamlConvertible {
 
     /**
      * Condition that marks the goal as accomplished when it matches.
+     * In JSON the condition type id is usually the field {@code type} (not {@code conditionTypeId}).
      *
      * @return the target condition
+     * @api.example {"type":"eventTypeCondition","parameterValues":{"eventTypeId":"purchase"}}
      */
     public Condition getTargetEvent() {
         return targetEvent;
diff --git a/api/src/main/java/org/apache/unomi/api/query/Query.java b/api/src/main/java/org/apache/unomi/api/query/Query.java
index 9e3e0fbe2..be9d1f16c 100644
--- a/api/src/main/java/org/apache/unomi/api/query/Query.java
+++ b/api/src/main/java/org/apache/unomi/api/query/Query.java
@@ -28,13 +28,44 @@ import java.io.Serializable;
  * and a {@code forceRefresh} flag that controls whether indexes are refreshed first.
  */
 public class Query implements Serializable {
+    /**
+     * Optional full-text filter applied to the search.
+     * @api.example Ada
+     */
     private String text;
+    /**
+     * Zero-based index of the first result to return.
+     * @api.example 0
+     */
     private int offset;
+    /**
+     * Maximum number of results to return, or {@code -1} for all matches.
+     * @api.example 20
+     */
     private int limit = Integer.MIN_VALUE;
+    /**
+     * Comma-separated sort fields with optional {@code :asc} or {@code :desc} (for example {@code properties.firstName:asc}).
+     * @api.example properties.firstName:asc
+     */
     private String sortby;
+    /**
+     * Condition that returned items must satisfy.
+     */
     private Condition condition;
+    /**
+     * When {@code true}, refresh indexes before running the query (slower, more consistent).
+     * @api.example false
+     */
     private boolean forceRefresh;
+    /**
+     * Scroll keep-alive window (for example {@code 10m}) when using scroll search.
+     * @api.example 10m
+     */
     private String scrollTimeValidity;
+    /**
+     * Scroll identifier returned by a previous scroll page when continuing deep pagination.
+     * @api.example DXF1ZXJ5QW5kRmV0Y2gBAAAAAAAAAD4WYmRUMAkwZGY=
+     */
     private String scrollIdentifier;
 
     /**
diff --git a/api/src/main/java/org/apache/unomi/api/rules/Rule.java b/api/src/main/java/org/apache/unomi/api/rules/Rule.java
index 1e2586f18..3e9a25b83 100644
--- a/api/src/main/java/org/apache/unomi/api/rules/Rule.java
+++ b/api/src/main/java/org/apache/unomi/api/rules/Rule.java
@@ -49,18 +49,46 @@ public class Rule extends MetadataItem implements YamlConvertible {
      */
     public static final String ITEM_TYPE = "rule";
     private static final long serialVersionUID = 3058739939263056507L;
+    /**
+     * Condition that must match before this rule's actions run.
+     * Typical trigger: {@code eventTypeCondition} with {@code eventTypeId}.
+     */
     private Condition condition;
 
+    /**
+     * Actions performed when the condition matches (ordered).
+     * @api.example [{"type":"setPropertyAction","parameterValues":{"setPropertyName":"properties.isPremium","setPropertyValueBoolean":true,"storeInSession":false}}]
+     */
     private List<Action> actions;
 
+    /**
+     * Identifiers of items linked to this rule (for example segments or goals that own it).
+     * @api.example ["goal-welcome"]
+     */
     private List<String> linkedItems;
 
+    /**
+     * When {@code true}, raise the rule-triggered event at most once per profile.
+     * @api.example false
+     */
     private boolean raiseEventOnlyOnceForProfile = false;
 
+    /**
+     * When {@code true}, raise the rule-triggered event at most once per session.
+     * @api.example false
+     */
     private boolean raiseEventOnlyOnceForSession = false;
 
+    /**
+     * When {@code true}, raise the rule-triggered event at most once overall.
+     * @api.example false
+     */
     private boolean raiseEventOnlyOnce = false;
 
+    /**
+     * Execution priority among matching rules (higher runs first when comparable).
+     * @api.example 0
+     */
     private int priority;
 
     /**
diff --git a/api/src/main/java/org/apache/unomi/api/rules/RuleStatistics.java b/api/src/main/java/org/apache/unomi/api/rules/RuleStatistics.java
index 80cf18ac4..bdd886bd1 100644
--- a/api/src/main/java/org/apache/unomi/api/rules/RuleStatistics.java
+++ b/api/src/main/java/org/apache/unomi/api/rules/RuleStatistics.java
@@ -38,12 +38,40 @@ public class RuleStatistics extends Item {
     public static final String ITEM_TYPE = "rulestats";
     private static final long serialVersionUID = 1L;
 
+    /**
+     * Cluster-wide execution count (excluding unsynchronized local count).
+     * @api.example 42
+     */
     private long executionCount = 0;
+    /**
+     * Executions on this node since {@link #lastSyncDate}.
+     * @api.example 3
+     */
     private long localExecutionCount = 0;
+    /**
+     * Cluster-wide time spent evaluating conditions, in milliseconds.
+     * @api.example 120
+     */
     private long conditionsTime = 0;
+    /**
+     * Condition evaluation time on this node since last sync, in milliseconds.
+     * @api.example 8
+     */
     private long localConditionsTime = 0;
+    /**
+     * Cluster-wide time spent running actions, in milliseconds.
+     * @api.example 95
+     */
     private long actionsTime = 0;
+    /**
+     * Action execution time on this node since last sync, in milliseconds.
+     * @api.example 5
+     */
     private long localActionsTime = 0;
+    /**
+     * When local counters were last merged into cluster totals (ISO-8601 in JSON).
+     * @api.example 2024-06-15T11:00:00.000Z
+     */
     private Date lastSyncDate;
 
     /**
diff --git a/api/src/main/java/org/apache/unomi/api/segments/DependentMetadata.java b/api/src/main/java/org/apache/unomi/api/segments/DependentMetadata.java
index 30926539f..b9391a5eb 100644
--- a/api/src/main/java/org/apache/unomi/api/segments/DependentMetadata.java
+++ b/api/src/main/java/org/apache/unomi/api/segments/DependentMetadata.java
@@ -29,8 +29,16 @@ import java.util.List;
  */
 public class DependentMetadata implements Serializable {
 
+    /**
+     * Segments that reference the given segment (for example via profileSegmentCondition).
+     * @api.example [{"id":"premium-buyers","name":"Premium buyers","scope":"mysite","enabled":true}]
+     */
     private List<Metadata> segments;
 
+    /**
+     * Scorings that reference the given segment.
+     * @api.example [{"id":"premium-score","name":"Premium score","scope":"mysite","enabled":true}]
+     */
     private List<Metadata> scorings;
 
     /**
@@ -48,6 +56,7 @@ public class DependentMetadata implements Serializable {
      * Metadata for segments that depend on the edited item.
      *
      * @return the dependent segment metadata
+     * @api.example [{"id":"premium-buyers","name":"Premium buyers","scope":"mysite","enabled":true}]
      */
     public List<Metadata> getSegments() {
         return segments;
@@ -66,6 +75,7 @@ public class DependentMetadata implements Serializable {
      * Metadata for scorings that depend on the edited item.
      *
      * @return the dependent scoring metadata
+     * @api.example [{"id":"premium-score","name":"Premium score","scope":"mysite","enabled":true}]
      */
     public List<Metadata> getScorings() {
         return scorings;
diff --git a/api/src/main/java/org/apache/unomi/api/segments/Scoring.java b/api/src/main/java/org/apache/unomi/api/segments/Scoring.java
index 85e36ad1e..bb4455a18 100644
--- a/api/src/main/java/org/apache/unomi/api/segments/Scoring.java
+++ b/api/src/main/java/org/apache/unomi/api/segments/Scoring.java
@@ -42,6 +42,10 @@ public class Scoring extends MetadataItem implements YamlConvertible {
      */
     public static final String ITEM_TYPE = "scoring";
     private static final long serialVersionUID = 6351058906259967559L;
+    /**
+     * Scoring elements that define conditions and point values.
+     * @api.example [{"condition":{"type":"eventTypeCondition","parameterValues":{"eventTypeId":"view"}},"value":10}]
+     */
     private List<ScoringElement> elements;
 
     /**
@@ -61,8 +65,10 @@ public class Scoring extends MetadataItem implements YamlConvertible {
 
     /**
      * Scoring elements that define conditions and point values.
+     * Each element pairs a {@code condition} ({@code type} + {@code parameterValues}) with a point {@code value}.
      *
      * @return the scoring elements
+     * @api.example [{"condition":{"type":"eventTypeCondition","parameterValues":{"eventTypeId":"view"}},"value":10}]
      */
     public List<ScoringElement> getElements() {
         return elements;
diff --git a/api/src/main/java/org/apache/unomi/api/segments/ScoringElement.java b/api/src/main/java/org/apache/unomi/api/segments/ScoringElement.java
index 7467f5788..6fbe96074 100644
--- a/api/src/main/java/org/apache/unomi/api/segments/ScoringElement.java
+++ b/api/src/main/java/org/apache/unomi/api/segments/ScoringElement.java
@@ -35,6 +35,10 @@ import static org.apache.unomi.api.utils.YamlUtils.toYamlValue;
  * profile matches the element's condition.
  */
 public class ScoringElement implements Serializable, YamlConvertible {
+    /**
+     * Condition evaluated to decide whether this scoring element applies (JSON wire field {@code type} + {@code parameterValues}).
+     * @api.example {"type":"eventTypeCondition","parameterValues":{"eventTypeId":"view"}}
+     */
     private Condition condition;
     private int value;
 
@@ -46,8 +50,10 @@ public class ScoringElement implements Serializable, YamlConvertible {
 
     /**
      * Condition evaluated to decide whether this scoring element applies.
+     * In JSON the condition type id is usually the field {@code type} (not {@code conditionTypeId}).
      *
      * @return the condition
+     * @api.example {"type":"eventTypeCondition","parameterValues":{"eventTypeId":"view"}}
      */
     public Condition getCondition() {
         return condition;
diff --git a/api/src/main/java/org/apache/unomi/api/segments/Segment.java b/api/src/main/java/org/apache/unomi/api/segments/Segment.java
index b5a4758ab..cb3749cec 100644
--- a/api/src/main/java/org/apache/unomi/api/segments/Segment.java
+++ b/api/src/main/java/org/apache/unomi/api/segments/Segment.java
@@ -46,6 +46,10 @@ public class Segment extends MetadataItem implements YamlConvertible {
      */
     public static final String ITEM_TYPE = "segment";
     private static final long serialVersionUID = -1384533444860961296L;
+    /**
+     * Membership condition evaluated against profiles (JSON wire field {@code type} + {@code parameterValues}).
+     * @api.example {"type":"profilePropertyCondition","parameterValues":{"propertyName":"properties.isPremium","comparisonOperator":"equals","propertyValue":"true"}}
+     */
     private Condition condition;
 
     /**
@@ -65,8 +69,10 @@ public class Segment extends MetadataItem implements YamlConvertible {
 
     /**
      * Condition that profiles must satisfy to belong to this segment.
+     * In JSON the condition type id is usually the field {@code type} (not {@code conditionTypeId}).
      *
      * @return the membership condition
+     * @api.example {"type":"profilePropertyCondition","parameterValues":{"propertyName":"properties.isPremium","comparisonOperator":"equals","propertyValue":"true"}}
      */
     public Condition getCondition() {
         return condition;
diff --git a/api/src/main/java/org/apache/unomi/api/services/PersonalizationService.java b/api/src/main/java/org/apache/unomi/api/services/PersonalizationService.java
index bcd9a238c..8b3919fb8 100644
--- a/api/src/main/java/org/apache/unomi/api/services/PersonalizationService.java
+++ b/api/src/main/java/org/apache/unomi/api/services/PersonalizationService.java
@@ -63,12 +63,28 @@ public interface PersonalizationService {
     PersonalizationResult personalizeList(Profile profile, Session session, PersonalizationRequest personalizationRequest);
 
     /**
-     * Personalization request
+     * Request to resolve an ordered set of content variants for a profile/session
+     * (sent in {@code ContextRequest.personalizations}).
      */
     class PersonalizationRequest {
+        /**
+         * Client-defined personalization id; echoed as the key in {@code ContextResponse.personalizationResults}.
+         * @api.example homepage-hero
+         */
         private String id;
+        /**
+         * Strategy plugin name (for example {@code matching-content}, {@code score-sort}, {@code random}).
+         * @api.example matching-content
+         */
         private String strategy;
+        /**
+         * Strategy-specific options (control groups, fallbacks, size limits, …). Free-form map.
+         * @api.example {"fallback":"default","size":1}
+         */
         private Map<String, Object> strategyOptions;
+        /**
+         * Candidate content variants evaluated by the strategy.
+         */
         private List<PersonalizedContent> contents;
 
         /**
@@ -145,11 +161,22 @@ public interface PersonalizationService {
     }
 
     /**
-     * A personalizated content definition.
+     * One content variant (also used as entries in {@code ContextRequest.filters}).
      */
     class PersonalizedContent {
+        /**
+         * Variant / filter id returned to the client when selected or when evaluating filters.
+         * @api.example hero-a
+         */
         private String id;
+        /**
+         * Optional filters; all applicable conditions must match for the variant to be eligible.
+         */
         private List<Filter> filters;
+        /**
+         * Optional free-form metadata for the client (CMS labels, positions, …).
+         * @api.example {"position":"top"}
+         */
         private Map<String,Object> properties;
 
         /**
@@ -218,11 +245,21 @@ public interface PersonalizationService {
     }
 
     /**
-     * A filter definition for content filtering
+     * Filter applied to a content variant: condition plus optional target hints.
      */
     class Filter {
+        /**
+         * Optional targets describing which profile/session dimensions the filter concerns.
+         */
         private List<Target> appliesOn;
+        /**
+         * Unomi condition that must match the profile/session for this filter to pass.
+         */
         private Condition condition;
+        /**
+         * Optional free-form filter metadata.
+         * @api.example {"priority":1}
+         */
         private Map<String,Object> properties;
 
         /**
@@ -281,10 +318,18 @@ public interface PersonalizationService {
     }
 
     /**
-     * A target for content filtering.
+     * Named dimension and allowed values used as filter metadata (client/CMS hinting).
      */
     class Target {
+        /**
+         * Dimension name (for example a property key or {@code segments}).
+         * @api.example interests
+         */
         private String target;
+        /**
+         * Allowed values for that dimension.
+         * @api.example ["sports","news"]
+         */
         private List<String> values;
 
         /**
diff --git a/api/src/main/java/org/apache/unomi/api/tenants/ApiKey.java b/api/src/main/java/org/apache/unomi/api/tenants/ApiKey.java
index b6121792f..80b25002c 100644
--- a/api/src/main/java/org/apache/unomi/api/tenants/ApiKey.java
+++ b/api/src/main/java/org/apache/unomi/api/tenants/ApiKey.java
@@ -44,59 +44,57 @@ public class ApiKey extends Item {
     private static final SecureRandom SECURE_RANDOM = new SecureRandom();
 
     /**
-     * Enum defining the types of API keys.
+     * Scope of an API key. Serialized as {@code PUBLIC} or {@code PRIVATE}.
      */
     public enum ApiKeyType {
-        /**
-         * Public API key for context.json, event collector and other public-facing endpoints
-         */
+        /** Public key for context.json, event collector, and other public-facing endpoints. */
         PUBLIC,
-        
-        /**
-         * Private API key for protected endpoints including login and updateProperties
-         */
+
+        /** Private key for protected endpoints (login, updateProperties, admin-style operations). */
         PRIVATE
     }
 
     /**
      * SHA-256 hex digest of the API key ({@link org.apache.unomi.api.security.SecretHashService#hash(String)}).
-     * The plaintext key is never persisted; it is only returned once at creation time.
+     * The plaintext key is never persisted; it is only returned once at creation time via {@link ApiKeyCreationResult}.
      */
     private String keyHash;
 
     /**
-     * A display-safe, masked representation of the key (e.g. "unomi_v1_****ab12"),
-     * suitable for showing in UIs and logs without exposing the secret.
+     * Display-safe masked key (for example {@code unomi_v1_****ab12}) for UIs and logs.
+     * @api.example unomi_v1_****ab12
      */
     private String maskedKey;
 
     /**
-     * The type of API key (public or private).
+     * Key scope: {@code PUBLIC} or {@code PRIVATE}.
+     * @api.example PUBLIC
      */
     private ApiKeyType keyType;
 
     /**
-     * The name or identifier of the API key.
+     * Optional operator label for the key.
      */
     private String name;
 
     /**
-     * A description of the API key's purpose or usage.
+     * Optional description of the key's purpose.
      */
     private String description;
 
     /**
-     * The date when the API key was created.
+     * When the key was created (ISO-8601 date-time in JSON).
      */
     private Date creationDate;
 
     /**
-     * The date when the API key expires.
+     * Expiration instant, or omitted/null when the key does not expire.
      */
     private Date expirationDate;
 
     /**
-     * Whether the API key has been revoked.
+     * {@code true} if the key has been revoked and must not authenticate.
+     * @api.example false
      */
     private boolean revoked;
 
diff --git a/api/src/main/java/org/apache/unomi/api/tenants/ApiKeyCreationResult.java b/api/src/main/java/org/apache/unomi/api/tenants/ApiKeyCreationResult.java
index 8bbea6daa..66689ca3d 100644
--- a/api/src/main/java/org/apache/unomi/api/tenants/ApiKeyCreationResult.java
+++ b/api/src/main/java/org/apache/unomi/api/tenants/ApiKeyCreationResult.java
@@ -25,7 +25,15 @@ package org.apache.unomi.api.tenants;
  */
 public class ApiKeyCreationResult {
 
+    /**
+     * Persisted API key metadata (hash, maskedKey, type, dates). Does not contain the secret.
+     */
     private ApiKey apiKey;
+    /**
+     * One-time plaintext key value, only available at creation. Prefixed with {@code unomi_v1_}.
+     * Store immediately; it cannot be recovered later.
+     * @api.example unomi_v1_0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF
+     */
     private String plainTextKey;
 
     /**
diff --git a/api/src/main/java/org/apache/unomi/api/tenants/Tenant.java b/api/src/main/java/org/apache/unomi/api/tenants/Tenant.java
index c9c8abad9..2ea379863 100644
--- a/api/src/main/java/org/apache/unomi/api/tenants/Tenant.java
+++ b/api/src/main/java/org/apache/unomi/api/tenants/Tenant.java
@@ -35,54 +35,59 @@ public class Tenant extends Item {
     public static final String ITEM_TYPE = "tenant";
 
     /**
-     * The display name of the tenant.
+     * Display name of the tenant (human-readable label).
+     * @api.example Acme Corp
      */
     private String name;
 
     /**
-     * A description of the tenant's purpose or usage.
+     * Free-text description of the tenant's purpose or usage.
+     * @api.example Production tenant for Acme
      */
     private String description;
 
     /**
-     * The current operational status of the tenant.
+     * Lifecycle status. One of {@link TenantStatus}: {@code ACTIVE}, {@code DISABLED},
+     * {@code SUSPENDED}, {@code PENDING_ACTIVATION}, {@code MAINTENANCE}. New tenants are created as {@code ACTIVE}.
+     * @api.example ACTIVE
      */
     private TenantStatus status;
 
     /**
-     * The date when the tenant was created.
+     * When the tenant was created (ISO-8601 date-time in JSON).
      */
     private Date creationDate;
 
     /**
-     * The date when the tenant was last modified.
+     * When the tenant was last modified (ISO-8601 date-time in JSON).
      */
     private Date lastModificationDate;
 
 
     /**
-     * The list of all API keys (both active and historical) associated with the tenant.
-     * This list maintains a history of all API keys that have been generated for the tenant,
-     * including both public and private keys, for auditing purposes.
+     * API keys associated with the tenant (active and historical). Each entry is an {@link ApiKey}
+     * with hashed material ({@code keyHash}), display {@code maskedKey}, {@code keyType}
+     * ({@code PUBLIC} or {@code PRIVATE}), dates, and {@code revoked}. The plaintext secret is never stored here.
      */
     private List<ApiKey> apiKeys;
 
     /**
-     * Additional custom properties for the tenant.
+     * Open map of custom tenant properties. Free-form keys/values; often used for operator metadata
+     * (for example display {@code name}/{@code description} at create time via {@code TenantRequest}).
+     * @api.example {"region":"eu-west","tier":"premium"}
      */
     private Map<String, Object> properties;
 
     /**
-     * The set of event types that are restricted for this tenant.
-     * Events of these types will require IP validation before being processed.
-     * This is used to control which event types require additional validation
-     * at the tenant level.
+     * Event type names that require IP validation before processing for this tenant
+     * (for example {@code login} or custom event type identifiers). Empty means no extra restriction.
+     * @api.example ["login","updateProperties"]
      */
     private Set<String> restrictedEventTypes = new HashSet<>();
 
     /**
-     * The set of IP addresses or CIDR ranges that are authorized to make requests
-     * for this tenant. Requests from IP addresses not in this set will be rejected.
+     * Authorized client IP addresses or CIDR ranges. When non-empty, requests from other addresses are rejected.
+     * @api.example ["10.0.0.0/8","192.168.1.10"]
      */
     private Set<String> authorizedIPs = new HashSet<>();
 
diff --git a/api/src/main/java/org/apache/unomi/api/tenants/TenantEventPurgeResult.java b/api/src/main/java/org/apache/unomi/api/tenants/TenantEventPurgeResult.java
index eed555a7b..d2229b33b 100644
--- a/api/src/main/java/org/apache/unomi/api/tenants/TenantEventPurgeResult.java
+++ b/api/src/main/java/org/apache/unomi/api/tenants/TenantEventPurgeResult.java
@@ -23,9 +23,21 @@ package org.apache.unomi.api.tenants;
  */
 public class TenantEventPurgeResult {
 
+    /**
+     * Tenant this purge applies to.
+     * @api.example acme
+     */
     private String tenantId;
+    /**
+     * Retention cutoff in whole days (query {@code retentionDays}; minimum
+     * {@link TenantUsageService#MIN_EVENT_RETENTION_DAYS}).
+     * @api.example 30
+     */
     private int retentionDays;
-    /** Events matching the retention cutoff before delete-by-query was submitted; not a post-delete count. */
+    /**
+     * Events matching the retention cutoff before delete-by-query was submitted; not a post-delete count.
+     * @api.example 1200
+     */
     private long eventsMatched;
     /**
      * {@code true} if the delete-by-query completed successfully; {@code false} if the
@@ -33,7 +45,12 @@ public class TenantEventPurgeResult {
      * partial-success indicator: {@link #eventsMatched} is a pre-delete estimate only, so a
      * {@code true} result does not by itself confirm how many events were actually removed.
      */
+    /**
+     * {@code true} if delete-by-query completed successfully; {@code false} maps to HTTP 500 on the REST endpoint.
+     * @api.example true
+     */
     private boolean purgeRequested;
+    /** When the purge was requested (epoch millis, UTC). */
     private long requestedAt;
 
     /**
diff --git a/api/src/main/java/org/apache/unomi/api/tenants/TenantScopeUsage.java b/api/src/main/java/org/apache/unomi/api/tenants/TenantScopeUsage.java
index 057240412..85436b3e6 100644
--- a/api/src/main/java/org/apache/unomi/api/tenants/TenantScopeUsage.java
+++ b/api/src/main/java/org/apache/unomi/api/tenants/TenantScopeUsage.java
@@ -24,8 +24,14 @@ package org.apache.unomi.api.tenants;
  */
 public class TenantScopeUsage {
 
+    /**
+     * Scope identifier (site/application id), excluding {@code systemscope} at the aggregate level.
+     * @api.example mysite
+     */
     private String scopeId;
+    /** Number of segments in this scope at collection time. */
     private long segmentCount;
+    /** Number of rules in this scope at collection time. */
     private long ruleCount;
 
     /**
diff --git a/api/src/main/java/org/apache/unomi/api/tenants/TenantStatus.java b/api/src/main/java/org/apache/unomi/api/tenants/TenantStatus.java
index 021f71b6e..4ad721b4d 100644
--- a/api/src/main/java/org/apache/unomi/api/tenants/TenantStatus.java
+++ b/api/src/main/java/org/apache/unomi/api/tenants/TenantStatus.java
@@ -17,32 +17,22 @@
 package org.apache.unomi.api.tenants;
 
 /**
- * Lifecycle state of a tenant (for example active, suspended, or pending).
- * Administrators filter and manage tenants based on these enum values.
+ * Lifecycle state of a tenant. Serialized as the enum name string in JSON
+ * ({@code ACTIVE}, {@code DISABLED}, {@code SUSPENDED}, {@code PENDING_ACTIVATION}, {@code MAINTENANCE}).
  */
 public enum TenantStatus {
-    /**
-     * Tenant is active and fully operational
-     */
+    /** Fully operational; default for newly created tenants. */
     ACTIVE,
 
-    /**
-     * Tenant is disabled and cannot perform any operations
-     */
+    /** Disabled; cannot perform tenant operations. */
     DISABLED,
 
-    /**
-     * Tenant is temporarily suspended, typically due to policy violations or maintenance
-     */
+    /** Temporarily suspended (policy, billing, or maintenance). */
     SUSPENDED,
 
-    /**
-     * Tenant is created but waiting for activation process to complete
-     */
+    /** Created but waiting for activation to complete. */
     PENDING_ACTIVATION,
 
-    /**
-     * Tenant is undergoing scheduled maintenance
-     */
+    /** Undergoing scheduled maintenance. */
     MAINTENANCE
 }
diff --git a/api/src/main/java/org/apache/unomi/api/tenants/TenantUsage.java b/api/src/main/java/org/apache/unomi/api/tenants/TenantUsage.java
index 9313ecd8e..2cbaeaff1 100644
--- a/api/src/main/java/org/apache/unomi/api/tenants/TenantUsage.java
+++ b/api/src/main/java/org/apache/unomi/api/tenants/TenantUsage.java
@@ -29,27 +29,40 @@ import java.util.List;
  */
 public class TenantUsage {
 
+    /**
+     * Tenant this snapshot describes.
+     * @api.example acme
+     */
     private String tenantId;
-    /** Normalized period label, e.g. {@code 2026-07} for a calendar month. */
+    /**
+     * Normalized period label. For month windows typically {@code YYYY-MM} (for example {@code 2026-07}).
+     * Request {@code period} query accepts {@code current-month}, {@code YYYY-MM}, or legacy {@code 24h}.
+     * @api.example 2026-07
+     */
     private String period;
     /** Inclusive start of the reporting period (epoch millis, UTC). */
     private long periodStart;
     /** Exclusive end of the reporting period (epoch millis, UTC). */
     private long periodEnd;
+    /** Point-in-time profile count at collection. */
     private long profileCount;
-    /** Events in the calendar month identified by {@link #period}. */
+    /** Events whose {@code timeStamp} falls in [{@link #periodStart}, {@link #periodEnd}). */
     private long eventCount;
     /** Tenant scopes excluding {@code systemscope}. */
     private long scopeCount;
+    /** Point-in-time segment count. */
     private long segmentCount;
+    /** Point-in-time rule count. */
     private long ruleCount;
     /** Document count across tenant indices (not byte size). */
     private long storageDocumentCount;
-    /** Active API keys on the tenant record. */
+    /** Active (non-revoked, non-expired) API keys on the tenant record. */
     private long activeApiKeyCount;
-    /** In-memory REST request counter for this tenant since the Unomi process started. */
+    /** In-memory REST request counter for this tenant since the Unomi process started (resets on restart). */
     private long restRequestCount;
+    /** Per-scope segment/rule breakdown; empty when no scoped usage was collected. */
     private List<TenantScopeUsage> scopeUsages = new ArrayList<>();
+    /** When this snapshot was collected (epoch millis, UTC). */
     private long collectedAt;
 
     /**
diff --git a/extensions/geonames/rest/pom.xml b/extensions/geonames/rest/pom.xml
index 35d104956..6f0d50fc1 100644
--- a/extensions/geonames/rest/pom.xml
+++ b/extensions/geonames/rest/pom.xml
@@ -101,4 +101,18 @@
         </dependency>
     </dependencies>
 
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>dev.inoyu.openapi</groupId>
+                <artifactId>openapi-enrich-maven-plugin</artifactId>
+                <configuration>
+                    <extraSourceRoots>
+                        <extraSourceRoot>${project.basedir}/../../../api/src/main/java</extraSourceRoot>
+                    </extraSourceRoots>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
 </project>
diff --git a/extensions/geonames/rest/src/main/java/org/apache/unomi/geonames/rest/GeonamesEndPoint.java b/extensions/geonames/rest/src/main/java/org/apache/unomi/geonames/rest/GeonamesEndPoint.java
index 3f1ad8118..9bb5bf25a 100644
--- a/extensions/geonames/rest/src/main/java/org/apache/unomi/geonames/rest/GeonamesEndPoint.java
+++ b/extensions/geonames/rest/src/main/java/org/apache/unomi/geonames/rest/GeonamesEndPoint.java
@@ -34,6 +34,9 @@ import java.util.ArrayList;
 import java.util.List;
 import java.util.Locale;
 
+/**
+ * JAX-RS endpoint for GeoNames lookups (reverse geocoding, hierarchies, cities, capitals).
+ */
 @Produces(MediaType.APPLICATION_JSON + ";charset=UTF-8")
 @CrossOriginResourceSharing(
         allowAllOrigins = true,
@@ -56,6 +59,17 @@ public class GeonamesEndPoint {
         this.geonamesService = geonamesService;
     }
 
+    /**
+     * Reverse-geocodes a latitude/longitude pair.
+     * <p>
+     * {@code latlon} must be {@code lat,lon} (comma-separated). Country names may be localized via {@code Accept-Language}.
+     *
+     * @param latlon comma-separated latitude and longitude
+     * @param language optional {@code Accept-Language} header for localized country names
+     * @return matching geoname entries
+     * @api.status 200 array org.apache.unomi.geonames.services.GeonameEntry Matching entries (may be empty).
+     * @api.example [{"itemId":"2988507","itemType":"geonameEntry","name":"Paris","countryCode":"FR","featureCode":"PPLC"}]
+     */
     @GET
     @Path("/reverseGeoCode/{latlon}")
     public List<GeonameEntry> reverseGeoCode(@PathParam("latlon") String latlon, @HeaderParam("Accept-Language") String language) {
@@ -65,6 +79,15 @@ public class GeonamesEndPoint {
         return entries;
     }
 
+    /**
+     * Returns child geoname entries along a slash-separated hierarchy path.
+     *
+     * @param items path segments describing the parent hierarchy (e.g. continent/country/region)
+     * @param language optional {@code Accept-Language} header for localized country names
+     * @return child entries (up to 999)
+     * @api.status 200 org.apache.unomi.api.PartialList GeonameEntry page (list items are GeonameEntry; may be empty).
+     * @api.example {"list":[{"itemId":"3017382","itemType":"geonameEntry","name":"France","countryCode":"FR","featureCode":"PCLI"}],"offset":0,"pageSize":1,"totalSize":1,"totalSizeRelation":"EQUAL"}
+     */
     @GET
     @Path("/entries/{items:.*}")
     public PartialList<GeonameEntry> getChildrenEntries(@PathParam("items") List<PathSegment> items, @HeaderParam("Accept-Language") String language) {
@@ -77,6 +100,15 @@ public class GeonamesEndPoint {
         return list;
     }
 
+    /**
+     * Returns child city entries along a slash-separated hierarchy path.
+     *
+     * @param items path segments describing the parent hierarchy
+     * @param language optional {@code Accept-Language} header for localized country names
+     * @return child cities (up to 999)
+     * @api.status 200 org.apache.unomi.api.PartialList GeonameEntry page (list items are GeonameEntry; may be empty).
+     * @api.example {"list":[{"itemId":"2988507","itemType":"geonameEntry","name":"Paris","countryCode":"FR","featureCode":"PPLC"}],"offset":0,"pageSize":1,"totalSize":1,"totalSizeRelation":"EQUAL"}
+     */
     @GET
     @Path("/cities/{items:.*}")
     public PartialList<GeonameEntry> getChildrenCities(@PathParam("items") List<PathSegment> items, @HeaderParam("Accept-Language") String language) {
@@ -89,6 +121,15 @@ public class GeonamesEndPoint {
         return list;
     }
 
+    /**
+     * Returns the administrative hierarchy for the given geoname id.
+     *
+     * @param id the geoname identifier
+     * @param language optional {@code Accept-Language} header for localized country names
+     * @return hierarchy entries from root to the requested id
+     * @api.status 200 array org.apache.unomi.geonames.services.GeonameEntry Hierarchy entries (may be empty).
+     * @api.example [{"itemId":"6255148","itemType":"geonameEntry","name":"Europe","featureCode":"CONT"},{"itemId":"3017382","itemType":"geonameEntry","name":"France","countryCode":"FR","featureCode":"PCLI"}]
+     */
     @GET
     @Path("/hierarchy/{id}")
     public List<GeonameEntry> getHierarchy(@PathParam("id") String id, @HeaderParam("Accept-Language") String language) {
@@ -97,6 +138,15 @@ public class GeonamesEndPoint {
         return list;
     }
 
+    /**
+     * Returns capital entries for the given parent geoname id.
+     *
+     * @param id the parent geoname identifier
+     * @param language optional {@code Accept-Language} header for localized country names
+     * @return capital entries
+     * @api.status 200 array org.apache.unomi.geonames.services.GeonameEntry Capital entries (may be empty).
+     * @api.example [{"itemId":"2988507","itemType":"geonameEntry","name":"Paris","countryCode":"FR","featureCode":"PPLC"}]
+     */
     @GET
     @Path("/capitals/{id}")
     public List<GeonameEntry> getCapitalEntries(@PathParam("id") String id, @HeaderParam("Accept-Language") String language) {
diff --git a/extensions/groovy-actions/rest/pom.xml b/extensions/groovy-actions/rest/pom.xml
index e3620c88b..3bedcc985 100644
--- a/extensions/groovy-actions/rest/pom.xml
+++ b/extensions/groovy-actions/rest/pom.xml
@@ -109,6 +109,15 @@
     </dependencies>
     <build>
         <plugins>
+            <plugin>
+                <groupId>dev.inoyu.openapi</groupId>
+                <artifactId>openapi-enrich-maven-plugin</artifactId>
+                <configuration>
+                    <extraSourceRoots>
+                        <extraSourceRoot>${project.basedir}/../../../api/src/main/java</extraSourceRoot>
+                    </extraSourceRoots>
+                </configuration>
+            </plugin>
             <plugin>
                 <groupId>org.apache.felix</groupId>
                 <artifactId>maven-bundle-plugin</artifactId>
diff --git a/extensions/groovy-actions/rest/src/main/java/org/apache/unomi/groovy/actions/rest/GroovyActionsEndPoint.java b/extensions/groovy-actions/rest/src/main/java/org/apache/unomi/groovy/actions/rest/GroovyActionsEndPoint.java
index 8ba9ca26e..1f41cf6f3 100644
--- a/extensions/groovy-actions/rest/src/main/java/org/apache/unomi/groovy/actions/rest/GroovyActionsEndPoint.java
+++ b/extensions/groovy-actions/rest/src/main/java/org/apache/unomi/groovy/actions/rest/GroovyActionsEndPoint.java
@@ -52,10 +52,15 @@ public class GroovyActionsEndPoint {
     }
 
     /**
-     * Save a groovy action file and create an actionType entry to allow to call this action
+     * Uploads a Groovy action script and registers a matching action type.
+     * <p>
+     * The multipart field {@code file} must be a {@code .groovy} file; the action id is derived from the filename.
      *
-     * @param file the file to save
-     * @return Response of the API call
+     * @param file the Groovy script upload
+     * @return an empty success response
+     * @api.status 200 empty Action saved and action type registered.
+     * @api.status 500 empty Error reading or processing the uploaded file.
+     * @api.example {}
      */
     @POST
     @Path("/")
@@ -73,9 +78,11 @@ public class GroovyActionsEndPoint {
     }
 
     /**
-     * Deletes the rule identified by the specified identifier.
+     * Deletes the Groovy action and its action type entry.
      *
-     * @param actionId the identifier of the groovy action that we want to delete
+     * @param actionId the action identifier
+     * @api.status 204 empty Action deleted.
+     * @api.example {}
      */
     @DELETE
     @Path("/{actionId}")
diff --git a/extensions/healthcheck/pom.xml b/extensions/healthcheck/pom.xml
index 49f88304d..cf97e9c66 100644
--- a/extensions/healthcheck/pom.xml
+++ b/extensions/healthcheck/pom.xml
@@ -166,6 +166,18 @@
                     </execution>
                 </executions>
             </plugin>
+            <plugin>
+                <groupId>dev.inoyu.openapi</groupId>
+                <artifactId>openapi-enrich-maven-plugin</artifactId>
+                <configuration>
+                    <servletPaths>
+                        <servletPath>
+                            <className>org.apache.unomi.healthcheck.servlet.HealthCheckServlet</className>
+                            <path>/health/check</path>
+                        </servletPath>
+                    </servletPaths>
+                </configuration>
+            </plugin>
         </plugins>
     </build>
 </project>
diff --git a/extensions/healthcheck/src/main/java/org/apache/unomi/healthcheck/HealthCheckResponse.java b/extensions/healthcheck/src/main/java/org/apache/unomi/healthcheck/HealthCheckResponse.java
index 1201a60b2..b8cb1ebf4 100644
--- a/extensions/healthcheck/src/main/java/org/apache/unomi/healthcheck/HealthCheckResponse.java
+++ b/extensions/healthcheck/src/main/java/org/apache/unomi/healthcheck/HealthCheckResponse.java
@@ -23,13 +23,45 @@ import java.util.LinkedHashMap;
 import java.util.Map;
 
 /**
- * A Health Check response.
+ * One liveliness check result returned by GET /health/check.
  */
 public class HealthCheckResponse {
 
+    /**
+     * Name of the check. Built-ins include {@code karaf} (always present) plus
+     * enabled providers such as {@code elasticsearch}, {@code opensearch},
+     * {@code cluster}, {@code persistence}, and {@code unomi} (bundle state).
+     * @api.example elasticsearch
+     */
     private final String name;
+    /**
+     * Check outcome. One of: {@code LIVE} (ready to serve traffic),
+     * {@code UP} (running or still starting), {@code DOWN} (unavailable),
+     * {@code ERROR} (the check itself failed). The HTTP endpoint returns 200
+     * only when every item is {@code LIVE}; otherwise 206.
+     * @api.example LIVE
+     */
     private final Status status;
+    /**
+     * Time spent collecting this check, in milliseconds.
+     * @api.example 42
+     */
     private final long collectingTime;
+    /**
+     * Optional provider-specific details; omitted from JSON when empty.
+     * Values are strings, numbers, or booleans. Common keys:
+     * {@code error} / {@code error.cause} on failure or timeout;
+     * elasticsearch/opensearch: {@code cluster_name}, {@code status}
+     * (green/yellow/red), {@code timed_out}, {@code number_of_nodes},
+     * {@code number_of_data_nodes}, {@code active_primary_shards},
+     * {@code active_shards}, {@code relocating_shards},
+     * {@code initializing_shards}, {@code unassigned_shards};
+     * cluster: {@code cluster.size}, {@code cluster.node.<i>.uptime},
+     * {@code cluster.node.<i>.cpuload}, {@code cluster.node.<i>.loadAverage},
+     * {@code cluster.node.<i>.public}, {@code cluster.node.<i>.internal},
+     * {@code cluster.node.<i>.role}.
+     * @api.example {"cluster_name":"elasticsearch","status":"green","number_of_nodes":1}
+     */
     private final Map<String, Object> data;
 
     protected HealthCheckResponse(String name, Status status, long collectingTime, Map<String, Object> data) {
@@ -156,11 +188,18 @@ public class HealthCheckResponse {
         }
     }
 
+    /**
+     * Outcome of a single health check.
+     */
     public enum Status {
-        DOWN,     //Not available
-        UP,       //Running or starting
-        LIVE,     //Ready to serve requests
-        ERROR     //Errors during check
+        /** Not available / not ready. */
+        DOWN,
+        /** Running or still starting (not yet ready to serve traffic). */
+        UP,
+        /** Ready to serve requests; required on every check for HTTP 200. */
+        LIVE,
+        /** The check itself failed (see {@code data.error} / {@code data.error.cause}). */
+        ERROR
     }
 
 }
diff --git a/extensions/healthcheck/src/main/java/org/apache/unomi/healthcheck/servlet/HealthCheckServlet.java b/extensions/healthcheck/src/main/java/org/apache/unomi/healthcheck/servlet/HealthCheckServlet.java
index b25deb56c..e1f5f3755 100644
--- a/extensions/healthcheck/src/main/java/org/apache/unomi/healthcheck/servlet/HealthCheckServlet.java
+++ b/extensions/healthcheck/src/main/java/org/apache/unomi/healthcheck/servlet/HealthCheckServlet.java
@@ -35,7 +35,17 @@ import java.io.IOException;
 import java.util.List;
 
 /**
- * A simple servlet that provides a health check endpoint.
+ * Liveliness checks for Unomi, registered at /health/check via the OSGi HttpService.
+ * Callers must have the OSGi role "health"; otherwise the servlet returns HTTP 403.
+ * On success the body is a JSON array of health check results.
+ *
+ * <p>HTTP 200 is returned when every check reports LIVE. HTTP 206 Partial Content is
+ * returned when at least one check is not LIVE (still starting, down, or in error).
+ *
+ * @api.status 200 array org.apache.unomi.healthcheck.HealthCheckResponse All checks report LIVE.
+ * @api.status 206 array org.apache.unomi.healthcheck.HealthCheckResponse At least one check is not LIVE (HTTP 206 Partial Content).
+ * @api.status 403 empty Missing the OSGi health role.
+ * @api.example [{"name":"karaf","status":"LIVE","collectingTime":1},{"name":"elasticsearch","status":"LIVE","collectingTime":42,"data":{"cluster_name":"elasticsearch","status":"green","number_of_nodes":1}}]
  */
 public class HealthCheckServlet extends HttpServlet {
 
diff --git a/extensions/json-schema/rest/pom.xml b/extensions/json-schema/rest/pom.xml
index de0ef217a..dcd7c4ade 100644
--- a/extensions/json-schema/rest/pom.xml
+++ b/extensions/json-schema/rest/pom.xml
@@ -98,6 +98,15 @@
     </dependencies>
     <build>
         <plugins>
+            <plugin>
+                <groupId>dev.inoyu.openapi</groupId>
+                <artifactId>openapi-enrich-maven-plugin</artifactId>
+                <configuration>
+                    <extraSourceRoots>
+                        <extraSourceRoot>${project.basedir}/../../../api/src/main/java</extraSourceRoot>
+                    </extraSourceRoots>
+                </configuration>
+            </plugin>
             <plugin>
                 <groupId>org.apache.felix</groupId>
                 <artifactId>maven-bundle-plugin</artifactId>
diff --git a/extensions/json-schema/rest/src/main/java/org/apache/unomi/schema/rest/JsonSchemaEndPoint.java b/extensions/json-schema/rest/src/main/java/org/apache/unomi/schema/rest/JsonSchemaEndPoint.java
index 51dc39a48..2c221d0d3 100644
--- a/extensions/json-schema/rest/src/main/java/org/apache/unomi/schema/rest/JsonSchemaEndPoint.java
+++ b/extensions/json-schema/rest/src/main/java/org/apache/unomi/schema/rest/JsonSchemaEndPoint.java
@@ -54,9 +54,11 @@ public class JsonSchemaEndPoint {
     }
 
     /**
-     * Get the list of installed Json Schema Ids
+     * Returns identifiers of all installed JSON schemas.
      *
-     * @return A Set of JSON schema ids
+     * @return installed JSON schema ids
+     * @api.status 200 array empty Schema id set (may be empty).
+     * @api.example ["profile","event"]
      */
     @GET
     @Path("/")
@@ -65,10 +67,14 @@ public class JsonSchemaEndPoint {
     }
 
     /**
-     * Get a schema by it's id
+     * Returns the JSON schema document for the given id.
+     * <p>
+     * Request body is the schema id as a plain JSON string. When no schema matches, returns {@code null} (HTTP 200 with empty body).
      *
-     * @param id of the schema
-     * @return Json schema matching the id
+     * @param id the schema identifier
+     * @return the schema JSON string, or {@code null} when missing
+     * @api.status 200 empty Schema JSON string, or empty body when missing.
+     * @api.example "{\"$schema\":\"http://json-schema.org/draft-07/schema#\",\"type\":\"object\"}"
      */
     @POST
     @Path("/query")
@@ -81,10 +87,13 @@ public class JsonSchemaEndPoint {
     }
 
     /**
-     * Save a JSON schema
+     * Saves a JSON schema document.
      *
-     * @param jsonSchema the schema as string to save
-     * @return Response of the API call
+     * @param jsonSchema the schema JSON as plain text or JSON body
+     * @return an empty success response
+     * @api.status 200 empty Schema saved.
+     * @api.status 400 empty Invalid or unreadable schema body.
+     * @api.example "{\"$schema\":\"https://json-schema.org/draft/2019-09/schema\",\"self\":{\"vendor\":\"com.example\",\"target\":\"events\",\"name\":\"customEvent\",\"format\":\"jsonschema\",\"version\":\"1-0-0\"},\"title\":\"CustomEvent\",\"type\":\"object\",\"allOf\":[{\"$ref\":\"https://unomi.apache.org/schemas/json/event/1-0-0\"}],\"properties\":{\"properties\":{\"type\":\"object\",\"properties\":{\"category\":{\"type\":\"string\"}}}},\"unevaluatedProperties\":false}"
      */
     @POST
     @Path("/")
@@ -100,9 +109,14 @@ public class JsonSchemaEndPoint {
     }
 
     /**
-     * Deletes a JSON schema by it's id.
+     * Deletes the JSON schema with the given id.
+     * <p>
+     * Request body is the schema id as a plain JSON string.
      *
-     * @param id the identifier of the JSON schema that we want to delete
+     * @param id the schema identifier
+     * @return {@code true} when the schema was deleted, {@code false} when it did not exist
+     * @api.status 200 empty Deletion result flag.
+     * @api.example true
      */
     @POST
     @Path("/delete")
@@ -111,10 +125,13 @@ public class JsonSchemaEndPoint {
     }
 
     /**
-     * Being able to validate a given event is useful when you want to develop custom events and associated schemas
+     * Validates a single event JSON document against installed schemas.
      *
-     * @param event the event to be validated
-     * @return Validation error messages if there is some
+     * @param event the event JSON to validate
+     * @return validation errors (empty collection when valid)
+     * @api.status 200 array org.apache.unomi.schema.api.ValidationError Validation errors (may be empty).
+     * @api.status 400 empty Invalid or unreadable event body.
+     * @api.example [{"error":"$.eventType: is missing but it is required"}]
      */
     @POST
     @Produces(MediaType.APPLICATION_JSON + ";charset=UTF-8")
@@ -130,10 +147,13 @@ public class JsonSchemaEndPoint {
     }
 
     /**
-     * Being able to validate a given list of event is useful when you want to develop custom events and associated schemas
+     * Validates a batch of events JSON against installed schemas.
      *
-     * @param events the events to be validated
-     * @return Validation error messages if there is some grouped per event type
+     * @param events the events JSON to validate
+     * @return validation errors grouped by event type (empty map when all valid)
+     * @api.status 200 empty Event type to validation error set map (may be empty).
+     * @api.status 400 empty Invalid or unreadable events body.
+     * @api.example {"view":{"error":"$.scope: is missing but it is required"}}
      */
     @POST
     @Produces(MediaType.APPLICATION_JSON + ";charset=UTF-8")
diff --git a/extensions/lists-extension/rest/pom.xml b/extensions/lists-extension/rest/pom.xml
index ba6ae96e1..b069f3a18 100644
--- a/extensions/lists-extension/rest/pom.xml
+++ b/extensions/lists-extension/rest/pom.xml
@@ -95,4 +95,18 @@
         </dependency>
     </dependencies>
 
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>dev.inoyu.openapi</groupId>
+                <artifactId>openapi-enrich-maven-plugin</artifactId>
+                <configuration>
+                    <extraSourceRoots>
+                        <extraSourceRoot>${project.basedir}/../../../api/src/main/java</extraSourceRoot>
+                    </extraSourceRoots>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
 </project>
diff --git a/extensions/lists-extension/rest/src/main/java/org/apache/unomi/lists/rest/UserListServiceEndPoint.java b/extensions/lists-extension/rest/src/main/java/org/apache/unomi/lists/rest/UserListServiceEndPoint.java
index bd14b4780..b151398e6 100644
--- a/extensions/lists-extension/rest/src/main/java/org/apache/unomi/lists/rest/UserListServiceEndPoint.java
+++ b/extensions/lists-extension/rest/src/main/java/org/apache/unomi/lists/rest/UserListServiceEndPoint.java
@@ -33,7 +33,7 @@ import javax.ws.rs.core.MediaType;
 
 
 /**
- * @author Christophe Laprun
+ * JAX-RS endpoint for static {@link UserList} CRUD and metadata queries.
  */
 @Produces(MediaType.APPLICATION_JSON)
 @CrossOriginResourceSharing(
@@ -57,30 +57,69 @@ public class UserListServiceEndPoint {
         this.userListService = userListService;
     }
 
+    /**
+     * Returns the first page of user list metadata (offset 0, size 50).
+     *
+     * @return user list metadata page
+     * @api.status 200 org.apache.unomi.api.PartialList Metadata page (list items are Metadata; may be empty).
+     * @api.example {"list":[{"id":"newsletter-subscribers","name":"Newsletter subscribers","scope":"mysite","enabled":true}],"offset":0,"pageSize":1,"totalSize":1,"totalSizeRelation":"EQUAL"}
+     */
     @GET
     @Path("/")
     public PartialList<Metadata> getListMetadatas() {
         return userListService.getListMetadatas(0, 50, null);
     }
 
+    /**
+     * Returns user list metadata matching the given query.
+     *
+     * @param query the query lists must match
+     * @return a paged list of matching metadata
+     * @api.status 200 org.apache.unomi.api.PartialList Metadata page (list items are Metadata).
+     * @api.status 400 empty Invalid or unreadable query body.
+     * @api.example {"list":[{"id":"newsletter-subscribers","name":"Newsletter subscribers","scope":"mysite","enabled":true}],"offset":0,"pageSize":1,"totalSize":1,"totalSizeRelation":"EQUAL"}
+     */
     @POST
     @Path("/query")
     public PartialList<Metadata> getListMetadatas(Query query) {
         return userListService.getListMetadatas(query);
     }
 
+    /**
+     * Returns the user list with the given ID.
+     * When the list does not exist the endpoint returns {@code null} (HTTP 200 with empty body).
+     *
+     * @param listId the list identifier
+     * @return the user list, or {@code null} when missing
+     * @api.status 200 org.apache.unomi.lists.UserList List found, or empty body when missing.
+     * @api.example {"itemId":"newsletter-subscribers","itemType":"userList","metadata":{"id":"newsletter-subscribers","name":"Newsletter subscribers","scope":"mysite","enabled":true}}
+     */
     @GET
     @Path("/{listId}")
     public UserList load(@PathParam("listId") String listId) {
         return userListService.load(listId);
     }
 
+    /**
+     * Persists the specified user list.
+     *
+     * @param list the list to save
+     * @api.status 204 empty List created or updated.
+     * @api.example {"itemId":"newsletter-subscribers","itemType":"userList","metadata":{"id":"newsletter-subscribers","name":"Newsletter subscribers","scope":"mysite","enabled":true}}
+     */
     @POST
     @Path("/")
     public void save(UserList list) {
         userListService.save(list);
     }
 
+    /**
+     * Deletes the user list with the given ID.
+     *
+     * @param listId the list identifier
+     * @api.status 204 empty List deleted.
+     * @api.example {"itemId":"newsletter-subscribers","itemType":"userList","metadata":{"id":"newsletter-subscribers","name":"Newsletter subscribers","scope":"mysite","enabled":true}}
+     */
     @DELETE
     @Path("/{listId}")
     public void delete(@PathParam("listId") String listId) {
diff --git a/extensions/privacy-extension/rest/pom.xml b/extensions/privacy-extension/rest/pom.xml
index 6f02f40c1..0386e5ef8 100644
--- a/extensions/privacy-extension/rest/pom.xml
+++ b/extensions/privacy-extension/rest/pom.xml
@@ -92,5 +92,19 @@
         </dependency>
     </dependencies>
 
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>dev.inoyu.openapi</groupId>
+                <artifactId>openapi-enrich-maven-plugin</artifactId>
+                <configuration>
+                    <extraSourceRoots>
+                        <extraSourceRoot>${project.basedir}/../../../api/src/main/java</extraSourceRoot>
+                    </extraSourceRoots>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
 
 </project>
diff --git a/extensions/privacy-extension/rest/src/main/java/org/apache/unomi/privacy/rest/PrivacyServiceEndPoint.java b/extensions/privacy-extension/rest/src/main/java/org/apache/unomi/privacy/rest/PrivacyServiceEndPoint.java
index f804ac164..79cfb3a3a 100644
--- a/extensions/privacy-extension/rest/src/main/java/org/apache/unomi/privacy/rest/PrivacyServiceEndPoint.java
+++ b/extensions/privacy-extension/rest/src/main/java/org/apache/unomi/privacy/rest/PrivacyServiceEndPoint.java
@@ -29,7 +29,8 @@ import javax.ws.rs.core.Response;
 import java.util.List;
 
 /**
- * REST API end point for privacy service
+ * REST endpoint for privacy / GDPR-style profile operations and server identity.
+ * Covers anonymous browsing, event filters, property erasure, anonymization, and profile deletion.
  */
 @Produces(MediaType.APPLICATION_JSON)
 @CrossOriginResourceSharing(
@@ -43,22 +44,55 @@ public class PrivacyServiceEndPoint {
     @Reference
     private PrivacyService privacyService;
 
+    /**
+     * Sets the privacy service.
+     *
+     * @param privacyService the privacy service
+     */
     public void setPrivacyService(PrivacyService privacyService) {
         this.privacyService = privacyService;
     }
 
+    /**
+     * Returns build/capability information for the Unomi server (first entry when multiple bundles report).
+     *
+     * @return server identity and capability metadata
+     * @api.status 200 org.apache.unomi.api.ServerInfo Unomi server info.
+     * @api.example {"serverIdentifier":"unomi","serverVersion":"3.1.0-SNAPSHOT","serverBuildNumber":"1","serverScmBranch":"main","capabilities":{},"eventTypes":[],"logoLines":[]}
+     */
     @GET
     @Path("/info")
     public ServerInfo getServerInfo() {
         return privacyService.getServerInfo();
     }
 
+    /**
+     * Returns build/capability information for all reporting server bundles.
+     *
+     * @return list of server info records (Unomi first)
+     * @api.status 200 array org.apache.unomi.api.ServerInfo Server info entries (may be a single Unomi entry).
+     * @api.example [{"serverIdentifier":"unomi","serverVersion":"3.1.0-SNAPSHOT","serverBuildNumber":"1","serverScmBranch":"main"}]
+     */
     @GET
     @Path("/infos")
     public List<ServerInfo> getServerInfos() {
         return privacyService.getServerInfos();
     }
 
+    /**
+     * Deletes or purges privacy-related data for a profile.
+     * <p>
+     * Flag precedence: {@code purgeAll=true} purges associated data then the profile;
+     * else {@code withData=true} deletes profile data without full purge;
+     * else only the profile record is deleted.
+     *
+     * @param profileId the profile identifier
+     * @param withData when {@code true} (and not purgeAll), delete associated browsing data as well
+     * @param purgeAll when {@code true}, purge all associated data then delete the profile
+     * @return HTTP 200 when the operation completes
+     * @api.status 200 empty Profile / data deletion completed.
+     * @api.example {}
+     */
     @DELETE
     @Path("/profiles/{profileId}")
     public Response deleteProfileData(@PathParam("profileId") String profileId, @QueryParam("withData") @DefaultValue("false") boolean withData,
@@ -73,18 +107,46 @@ public class PrivacyServiceEndPoint {
         return Response.ok().build();
     }
 
+    /**
+     * Anonymizes the profile in the given scope (fires anonymize events / clears identifying properties).
+     *
+     * @param profileId the profile identifier
+     * @param scope scope used when raising anonymization events
+     * @api.status 204 empty Profile anonymization requested.
+     * @api.example {}
+     */
     @POST
     @Path("/profiles/{profileId}/anonymize")
     public void anonymizeProfile(@PathParam("profileId") String profileId, @QueryParam("scope") String scope) {
         privacyService.anonymizeProfile(profileId, scope);
     }
 
+    /**
+     * Returns whether the profile requires anonymous browsing.
+     *
+     * @param profileId the profile identifier
+     * @return {@code true} when anonymous browsing is required
+     * @api.status 200 empty Anonymous-browsing flag.
+     * @api.example false
+     */
     @GET
     @Path("/profiles/{profileId}/anonymousBrowsing")
     public Boolean isAnonymousBrowsing(@PathParam("profileId") String profileId) {
         return privacyService.isRequireAnonymousBrowsing(profileId);
     }
 
+    /**
+     * Turns on anonymous browsing for the profile.
+     * When {@code anonymizePastBrowsing=true}, past browsing data is anonymized first.
+     *
+     * @param profileId the profile identifier
+     * @param past when {@code true}, anonymize historical browsing data before enabling the flag
+     * @param scope scope used for related events
+     * @return HTTP 200 when the flag was set; HTTP 500 when the service reports failure
+     * @api.status 200 empty Anonymous browsing enabled.
+     * @api.status 500 empty Failed to enable anonymous browsing.
+     * @api.example {}
+     */
     @POST
     @Path("/profiles/{profileId}/anonymousBrowsing")
     public Response activateAnonymousBrowsing(@PathParam("profileId") String profileId, @QueryParam("anonymizePastBrowsing") @DefaultValue("false") boolean past, @QueryParam("scope") String scope) {
@@ -95,6 +157,16 @@ public class PrivacyServiceEndPoint {
         return r ? Response.ok().build() : Response.serverError().build();
     }
 
+    /**
+     * Turns off anonymous browsing for the profile.
+     *
+     * @param profileId the profile identifier
+     * @param scope scope used for related events
+     * @return HTTP 200 when the flag was cleared; HTTP 500 when the service reports failure
+     * @api.status 200 empty Anonymous browsing disabled.
+     * @api.status 500 empty Failed to disable anonymous browsing.
+     * @api.example {}
+     */
     @DELETE
     @Path("/profiles/{profileId}/anonymousBrowsing")
     public Response deactivateAnonymousBrowsing(@PathParam("profileId") String profileId, @QueryParam("scope") String scope) {
@@ -102,12 +174,29 @@ public class PrivacyServiceEndPoint {
         return r ? Response.ok().build() : Response.serverError().build();
     }
 
+    /**
+     * Returns event type ids that are filtered (not collected) for this profile.
+     *
+     * @param profileId the profile identifier
+     * @return filtered event type identifiers
+     * @api.status 200 array empty Event type id strings (may be empty).
+     * @api.example ["view","login"]
+     */
     @GET
     @Path("/profiles/{profileId}/eventFilters")
     public List<String> getEventFilters(@PathParam("profileId") String profileId) {
         return privacyService.getFilteredEventTypes(profileId);
     }
 
+    /**
+     * Replaces the list of filtered event types for this profile.
+     *
+     * @param profileId the profile identifier
+     * @param eventFilters event type ids to filter (body JSON array of strings)
+     * @return HTTP 200 when stored
+     * @api.status 200 empty Event filters updated.
+     * @api.example ["view","login"]
+     */
     @POST
     @Consumes(MediaType.APPLICATION_JSON)
     @Path("/profiles/{profileId}/eventFilters")
@@ -116,6 +205,15 @@ public class PrivacyServiceEndPoint {
         return Response.ok().build();
     }
 
+    /**
+     * Removes a single property from the profile (privacy erasure of one field).
+     *
+     * @param profileId the profile identifier
+     * @param propertyName property path to remove (for example {@code email} or {@code properties.email})
+     * @return HTTP 200 when removed
+     * @api.status 200 empty Property removed from the profile.
+     * @api.example {}
+     */
     @DELETE
     @Path("/profiles/{profileId}/properties/{propertyName}")
     public Response removeProperty(@PathParam("profileId") String profileId, @PathParam("propertyName") String propertyName) {
diff --git a/extensions/router/router-rest/pom.xml b/extensions/router/router-rest/pom.xml
index cb56bf0ff..b7b9233db 100644
--- a/extensions/router/router-rest/pom.xml
+++ b/extensions/router/router-rest/pom.xml
@@ -107,6 +107,15 @@
 
     <build>
         <plugins>
+            <plugin>
+                <groupId>dev.inoyu.openapi</groupId>
+                <artifactId>openapi-enrich-maven-plugin</artifactId>
+                <configuration>
+                    <extraSourceRoots>
+                        <extraSourceRoot>${project.basedir}/../../../api/src/main/java</extraSourceRoot>
+                    </extraSourceRoots>
+                </configuration>
+            </plugin>
             <plugin>
                 <groupId>org.apache.felix</groupId>
                 <artifactId>maven-bundle-plugin</artifactId>
diff --git a/extensions/router/router-rest/src/main/java/org/apache/unomi/router/rest/AbstractConfigurationServiceEndpoint.java b/extensions/router/router-rest/src/main/java/org/apache/unomi/router/rest/AbstractConfigurationServiceEndpoint.java
index 7d180ee49..df966f634 100644
--- a/extensions/router/router-rest/src/main/java/org/apache/unomi/router/rest/AbstractConfigurationServiceEndpoint.java
+++ b/extensions/router/router-rest/src/main/java/org/apache/unomi/router/rest/AbstractConfigurationServiceEndpoint.java
@@ -23,16 +23,19 @@ import javax.ws.rs.core.MediaType;
 import java.util.List;
 
 /**
- * Created by amidani on 26/06/2017.
+ * Abstract JAX-RS base for router import/export configuration CRUD.
+ *
+ * @param <T> configuration item type
  */
 public abstract class AbstractConfigurationServiceEndpoint<T> {
 
     protected ImportExportConfigurationService<T> configurationService;
 
     /**
-     * Retrieves all the configurations.
+     * Returns all router configurations of this type.
      *
-     * @return all the configurations.
+     * @return all configurations (may be empty)
+     * @api.status 200 array empty Configuration list (may be empty).
      */
     @GET
     @Path("/")
@@ -42,6 +45,13 @@ public abstract class AbstractConfigurationServiceEndpoint<T> {
         return this.configurationService.getAll();
     }
 
+    /**
+     * Creates or updates a router configuration.
+     *
+     * @param configuration the configuration to save
+     * @return the persisted configuration
+     * @api.status 200 empty Configuration saved.
+     */
     @POST
     @Path("/")
     @Produces(MediaType.APPLICATION_JSON)
@@ -49,10 +59,12 @@ public abstract class AbstractConfigurationServiceEndpoint<T> {
     public abstract T saveConfiguration(T configuration);
 
     /**
-     * Retrieves a configuration by id.
+     * Returns the configuration with the given id.
+     * When it does not exist the endpoint returns {@code null} (HTTP 200 with empty body).
      *
-     * @param configId config id
-     * @return the configuration that matches the given id.
+     * @param configId the configuration identifier
+     * @return the configuration, or {@code null} when missing
+     * @api.status 200 empty Configuration found, or empty body when missing.
      */
     @GET
     @Path("/{configId}")
@@ -63,9 +75,10 @@ public abstract class AbstractConfigurationServiceEndpoint<T> {
     }
 
     /**
-     * Delete a configuration by id.
+     * Deletes the configuration with the given id.
      *
-     * @param configId config id
+     * @param configId the configuration identifier
+     * @api.status 204 empty Configuration deleted.
      */
     @DELETE
     @Path("/{configId}")
diff --git a/extensions/router/router-rest/src/main/java/org/apache/unomi/router/rest/ExportConfigurationServiceEndPoint.java b/extensions/router/router-rest/src/main/java/org/apache/unomi/router/rest/ExportConfigurationServiceEndPoint.java
index 317345209..4371e2d13 100644
--- a/extensions/router/router-rest/src/main/java/org/apache/unomi/router/rest/ExportConfigurationServiceEndPoint.java
+++ b/extensions/router/router-rest/src/main/java/org/apache/unomi/router/rest/ExportConfigurationServiceEndPoint.java
@@ -37,6 +37,7 @@ import java.security.KeyStoreException;
 import java.security.NoSuchAlgorithmException;
 import java.text.SimpleDateFormat;
 import java.util.Date;
+import java.util.List;
 
 /**
  * A JAX-RS endpoint to manage {@link ExportConfiguration}s.
@@ -75,9 +76,37 @@ public class ExportConfigurationServiceEndPoint extends AbstractConfigurationSer
     }
 
     /**
-     * Save the given export configuration.
+     * Returns all export configurations.
      *
-     * @return the export configuration saved.
+     * @return all export configurations (may be empty)
+     * @api.status 200 array org.apache.unomi.router.api.ExportConfiguration Export configuration list (may be empty).
+     * @api.example [{"itemId":"segment-export","itemType":"exportConfig","metadata":{"id":"segment-export","name":"Segment export","scope":"systemscope","enabled":true}}]
+     */
+    @Override
+    public List<ExportConfiguration> getConfigurations() {
+        return super.getConfigurations();
+    }
+
+    /**
+     * Returns the export configuration with the given id.
+     *
+     * @param configId the configuration identifier
+     * @return the export configuration, or {@code null} when missing
+     * @api.status 200 org.apache.unomi.router.api.ExportConfiguration Export configuration found, or empty body when missing.
+     * @api.example {"itemId":"segment-export","itemType":"exportConfig","metadata":{"id":"segment-export","name":"Segment export","scope":"systemscope","enabled":true}}
+     */
+    @Override
+    public ExportConfiguration getConfiguration(String configId) {
+        return super.getConfiguration(configId);
+    }
+
+    /**
+     * Creates or updates an export configuration and persists it for Camel routes.
+     *
+     * @param exportConfiguration the export configuration to save
+     * @return the persisted export configuration
+     * @api.status 200 org.apache.unomi.router.api.ExportConfiguration Export configuration saved.
+     * @api.example {"itemId":"segment-export","itemType":"exportConfig","metadata":{"id":"segment-export","name":"Segment export","scope":"systemscope","enabled":true}}
      */
     @Override
     public ExportConfiguration saveConfiguration(ExportConfiguration exportConfiguration) {
@@ -86,17 +115,25 @@ public class ExportConfigurationServiceEndPoint extends AbstractConfigurationSer
         return exportConfigSaved;
     }
 
+    /**
+     * Deletes the export configuration with the given id.
+     *
+     * @param configId the configuration identifier
+     * @api.status 204 empty Export configuration deleted.
+     * @api.example {"itemId":"segment-export","itemType":"exportConfig","metadata":{"id":"segment-export","name":"Segment export","scope":"systemscope","enabled":true}}
+     */
     @Override
     public void deleteConfiguration(String configId) {
         this.configurationService.delete(configId);
     }
 
     /**
-     * Save/Update the given import configuration.
-     * Prepare the file to be processed with Camel routes
+     * Runs a one-shot profile export for the given export configuration and returns CSV.
      *
-     * @param exportConfiguration configuration
-     * @return OK / NOK Http Code.
+     * @param exportConfiguration the export configuration describing the segment and columns
+     * @return CSV attachment with exported profiles
+     * @api.status 200 empty CSV export payload ({@code text/csv} with Content-Disposition attachment).
+     * @api.example itemId,properties.email\nprofile-1,user@example.com
      */
     @POST
     @Path("/oneshot")
diff --git a/extensions/router/router-rest/src/main/java/org/apache/unomi/router/rest/ImportConfigurationServiceEndPoint.java b/extensions/router/router-rest/src/main/java/org/apache/unomi/router/rest/ImportConfigurationServiceEndPoint.java
index fa87f47ad..e8d639df1 100644
--- a/extensions/router/router-rest/src/main/java/org/apache/unomi/router/rest/ImportConfigurationServiceEndPoint.java
+++ b/extensions/router/router-rest/src/main/java/org/apache/unomi/router/rest/ImportConfigurationServiceEndPoint.java
@@ -44,6 +44,8 @@ import java.security.KeyManagementException;
 import java.security.KeyStoreException;
 import java.security.NoSuchAlgorithmException;
 
+import java.util.List;
+
 /**
  * A JAX-RS endpoint to manage {@link ImportConfiguration}s.
  */
@@ -74,9 +76,37 @@ public class ImportConfigurationServiceEndPoint extends AbstractConfigurationSer
     }
 
     /**
-     * Save the given import configuration.
+     * Returns all import configurations.
+     *
+     * @return all import configurations (may be empty)
+     * @api.status 200 array org.apache.unomi.router.api.ImportConfiguration Import configuration list (may be empty).
+     * @api.example [{"itemId":"profile-csv-import","itemType":"importConfig","metadata":{"id":"profile-csv-import","name":"Profile CSV import","scope":"systemscope","enabled":true},"mergingProperty":"itemId"}]
+     */
+    @Override
+    public List<ImportConfiguration> getConfigurations() {
+        return super.getConfigurations();
+    }
+
+    /**
+     * Returns the import configuration with the given id.
+     *
+     * @param configId the configuration identifier
+     * @return the import configuration, or {@code null} when missing
+     * @api.status 200 org.apache.unomi.router.api.ImportConfiguration Import configuration found, or empty body when missing.
+     * @api.example {"itemId":"profile-csv-import","itemType":"importConfig","metadata":{"id":"profile-csv-import","name":"Profile CSV import","scope":"systemscope","enabled":true},"mergingProperty":"itemId"}
+     */
+    @Override
+    public ImportConfiguration getConfiguration(String configId) {
+        return super.getConfiguration(configId);
+    }
+
+    /**
+     * Creates or updates an import configuration and persists it for Camel routes.
      *
-     * @return the import configuration saved.
+     * @param importConfiguration the import configuration to save
+     * @return the persisted import configuration
+     * @api.status 200 org.apache.unomi.router.api.ImportConfiguration Import configuration saved.
+     * @api.example {"itemId":"profile-csv-import","itemType":"importConfig","metadata":{"id":"profile-csv-import","name":"Profile CSV import","scope":"systemscope","enabled":true},"mergingProperty":"itemId"}
      */
     @Override
     public ImportConfiguration saveConfiguration(ImportConfiguration importConfiguration) {
@@ -86,18 +116,30 @@ public class ImportConfigurationServiceEndPoint extends AbstractConfigurationSer
         return importConfigSaved;
     }
 
+    /**
+     * Deletes the import configuration with the given id.
+     *
+     * @param configId the configuration identifier
+     * @api.status 204 empty Import configuration deleted.
+     * @api.example {"itemId":"profile-csv-import","itemType":"importConfig","metadata":{"id":"profile-csv-import","name":"Profile CSV import","scope":"systemscope","enabled":true},"mergingProperty":"itemId"}
+     */
     @Override
     public void deleteConfiguration(String configId) {
         this.configurationService.delete(configId);
     }
 
     /**
-     * Save/Update the given import configuration.
-     * Prepare the file to be processed with Camel routes
+     * Uploads a one-shot CSV file for an existing import configuration.
+     * <p>
+     * The file is stored under the configured one-shot upload directory as {@code {importConfigId}.csv}
+     * for subsequent Camel processing.
      *
-     * @param file           file
-     * @param importConfigId config
-     * @return OK / NOK Http Code.
+     * @param importConfigId the import configuration id (multipart field)
+     * @param file the CSV file upload (multipart field)
+     * @return an empty success response
+     * @api.status 200 empty CSV uploaded and ready for processing.
+     * @api.status 500 empty Error writing the uploaded file.
+     * @api.example {}
      */
     @POST
     @Path("/oneshot")
diff --git a/extensions/salesforce-connector/rest/pom.xml b/extensions/salesforce-connector/rest/pom.xml
index aaa1fef1f..ee589fcac 100644
--- a/extensions/salesforce-connector/rest/pom.xml
+++ b/extensions/salesforce-connector/rest/pom.xml
@@ -109,6 +109,15 @@
 
     <build>
         <plugins>
+            <plugin>
+                <groupId>dev.inoyu.openapi</groupId>
+                <artifactId>openapi-enrich-maven-plugin</artifactId>
+                <configuration>
+                    <extraSourceRoots>
+                        <extraSourceRoot>${project.basedir}/../../../api/src/main/java</extraSourceRoot>
+                    </extraSourceRoots>
+                </configuration>
+            </plugin>
             <plugin>
                 <groupId>org.codehaus.mojo</groupId>
                 <artifactId>buildnumber-maven-plugin</artifactId>
diff --git a/extensions/salesforce-connector/rest/src/main/java/org/apache/unomi/sfdc/rest/SFDCEndPoint.java b/extensions/salesforce-connector/rest/src/main/java/org/apache/unomi/sfdc/rest/SFDCEndPoint.java
index d882a022c..e68239bc4 100644
--- a/extensions/salesforce-connector/rest/src/main/java/org/apache/unomi/sfdc/rest/SFDCEndPoint.java
+++ b/extensions/salesforce-connector/rest/src/main/java/org/apache/unomi/sfdc/rest/SFDCEndPoint.java
@@ -34,6 +34,9 @@ import java.util.Enumeration;
 import java.util.HashMap;
 import java.util.Map;
 
+/**
+ * JAX-RS endpoint exposing Salesforce connector bundle metadata and API limits.
+ */
 @Produces(MediaType.APPLICATION_JSON + ";charset=UTF-8")
 @CrossOriginResourceSharing(
         allowAllOrigins = true,
@@ -64,6 +67,13 @@ public class SFDCEndPoint {
         this.bundleContext = bundleContext;
     }
 
+    /**
+     * Returns OSGi bundle headers for the Salesforce connector extension.
+     *
+     * @return bundle header name to value map
+     * @api.status 200 empty Bundle header map.
+     * @api.example {"Bundle-Name":"Apache Unomi :: Extensions :: Salesforce Connector","Bundle-Version":"3.0.0"}
+     */
     @GET
     @Path("/version")
     public Map<String,String> getVersion() {
@@ -77,6 +87,13 @@ public class SFDCEndPoint {
         return versionInfo;
     }
 
+    /**
+     * Returns current Salesforce API limits from the connected org.
+     *
+     * @return limit name to usage snapshot map
+     * @api.status 200 empty Salesforce limits map (shape depends on the connected org).
+     * @api.example {"DailyApiRequests":{"Max":15000,"Remaining":14200}}
+     */
     @GET
     @Path("/limits")
     public Map<String,Object> getLimits() {
diff --git a/pom.xml b/pom.xml
index 00b844bdb..233143697 100644
--- a/pom.xml
+++ b/pom.xml
@@ -99,6 +99,7 @@
         <jakarta-json-api.version>2.1.2</jakarta-json-api.version>
         <swagger-ui.version>5.27.1</swagger-ui.version>
         <cxf.version>3.6.8</cxf.version>
+        <openapi.enrich.version>0.1.0-SNAPSHOT</openapi.enrich.version>
         <rs-api.version>2.1</rs-api.version>
         <groovy.version>4.0.28</groovy.version>
         <httpclient-osgi.version>4.5.14</httpclient-osgi.version>
@@ -921,6 +922,19 @@
                         <!-- Broken {@link}/HTML/syntax fail the build; missing @param/@return do not (use -Pjavadoc-tags-warn) -->
                         <failOnError>true</failOnError>
                         <failOnWarnings>false</failOnWarnings>
+                        <!-- UNOMI-963: OpenAPI enrichment packs read @api.status / @api.example from source -->
+                        <tags>
+                            <tag>
+                                <name>api.status</name>
+                                <placement>a</placement>
+                                <head>API Status:</head>
+                            </tag>
+                            <tag>
+                                <name>api.example</name>
+                                <placement>a</placement>
+                                <head>API Example:</head>
+                            </tag>
+                        </tags>
                     </configuration>
                 </plugin>
                 <plugin>
@@ -1027,6 +1041,19 @@
                     <artifactId>license-maven-plugin</artifactId>
                     <version>${license-maven-plugin.version}</version>
                 </plugin>
+                <plugin>
+                    <groupId>dev.inoyu.openapi</groupId>
+                    <artifactId>openapi-enrich-maven-plugin</artifactId>
+                    <version>${openapi.enrich.version}</version>
+                    <executions>
+                        <execution>
+                            <id>generate-openapi-doc</id>
+                            <goals>
+                                <goal>generate</goal>
+                            </goals>
+                        </execution>
+                    </executions>
+                </plugin>
                 <plugin>
                     <groupId>org.jasig.maven</groupId>
                     <artifactId>maven-notice-plugin</artifactId>
diff --git a/rest/pom.xml b/rest/pom.xml
index d57a09b24..9b15ad8b1 100644
--- a/rest/pom.xml
+++ b/rest/pom.xml
@@ -210,10 +210,34 @@
             <artifactId>mockito-core</artifactId>
             <scope>test</scope>
         </dependency>
+
+        <!-- UNOMI-963: OpenAPI Javadoc enrichment (embedded into this OSGi bundle) -->
+        <dependency>
+            <groupId>dev.inoyu.openapi</groupId>
+            <artifactId>cxf-openapi-enricher</artifactId>
+            <version>${openapi.enrich.version}</version>
+        </dependency>
+        <!-- Direct dep so parent Embed-Dependency=* embeds the model JAR too (not only transitive). -->
+        <dependency>
+            <groupId>dev.inoyu.openapi</groupId>
+            <artifactId>openapi-enrich-model</artifactId>
+            <version>${openapi.enrich.version}</version>
+        </dependency>
     </dependencies>
 
     <build>
         <plugins>
+            <plugin>
+                <groupId>dev.inoyu.openapi</groupId>
+                <artifactId>openapi-enrich-maven-plugin</artifactId>
+                <configuration>
+                    <extraSourceRoots>
+                        <extraSourceRoot>${project.basedir}/../api/src/main/java</extraSourceRoot>
+                        <!-- TraceNode on ContextResponse / EventCollectorResponse -->
+                        <extraSourceRoot>${project.basedir}/../tracing/tracing-api/src/main/java</extraSourceRoot>
+                    </extraSourceRoots>
+                </configuration>
+            </plugin>
             <plugin>
                 <groupId>org.codehaus.mojo</groupId>
                 <artifactId>build-helper-maven-plugin</artifactId>
@@ -244,9 +268,33 @@
                 <extensions>true</extensions>
                 <configuration>
                     <instructions>
+                        <!--
+                          Classloader design (UNOMI-963):
+
+                          Embed ONLY private deps that must not be shared OSGi APIs:
+                            - opencsv (pre-existing Unomi pattern)
+                            - cxf-openapi-enricher + openapi-enrich-model (Inoyu-owned)
+
+                          Do NOT embed Jackson / CXF / Swagger / SLF4J / OSGi — those stay
+                          Import-Package so EnrichingOpenApiCustomizer and CXF OpenApiFeature
+                          share the same OpenAPI/Jackson classes (avoids LinkageError).
+
+                          Exclude dev.inoyu.openapi.enrich.* from Import-Package: those packages
+                          are on Bundle-ClassPath via the embedded JARs, not exported bundles.
+
+                          Do not set Private-Package: it replaces BND defaults and can drop
+                          org.apache.unomi.rest.service.impl from this bundle.
+                        -->
                         <Import-Package>
+                            !dev.inoyu.openapi.enrich.*,
                             *
                         </Import-Package>
+                        <Embed-Dependency>
+                            opencsv,
+                            cxf-openapi-enricher,
+                            openapi-enrich-model
+                        </Embed-Dependency>
+                        <Embed-Transitive>false</Embed-Transitive>
                     </instructions>
                 </configuration>
             </plugin>
diff --git a/rest/src/main/java/org/apache/unomi/rest/endpoints/CampaignsServiceEndPoint.java b/rest/src/main/java/org/apache/unomi/rest/endpoints/CampaignsServiceEndPoint.java
index b40061e20..91397d3ac 100644
--- a/rest/src/main/java/org/apache/unomi/rest/endpoints/CampaignsServiceEndPoint.java
+++ b/rest/src/main/java/org/apache/unomi/rest/endpoints/CampaignsServiceEndPoint.java
@@ -73,6 +73,8 @@ public class CampaignsServiceEndPoint {
      * Returns metadata for all campaigns.
      *
      * @return campaign metadata for every stored campaign
+     * @api.status 200 array org.apache.unomi.api.Metadata Campaign metadata for all stored campaigns (may be empty).
+     * @api.example [{"id":"summer-sale","name":"Summer Sale","scope":"mysite","enabled":true}]
      */
     @GET
     @Path("/")
@@ -82,8 +84,13 @@ public class CampaignsServiceEndPoint {
 
     /**
      * Saves the specified campaign in the context server and creates associated {@link Rule}s if the campaign is enabled.
+     * Body is a full {@link Campaign}: {@code metadata}, {@code entryCondition} (JSON field {@code type} + {@code parameterValues}),
+     * date range, cost, and optional {@code primaryGoal}.
      *
      * @param campaign the Campaign to be saved
+     * @api.status 204 empty Campaign created or updated.
+     * @api.status 400 empty Invalid or unreadable campaign body.
+     * @api.example {"itemId":"summer-sale","itemType":"campaign","metadata":{"id":"summer-sale","name":"Summer Sale","scope":"mysite","enabled":true},"startDate":"2024-06-01T00:00:00.000Z","endDate":"2024-08-31T23:59:59.000Z","entryCondition":{"type":"eventTypeCondition","parameterValues":{"eventTypeId":"view"}},"cost":5000.0,"currency":"USD","primaryGoal":"checkout-goal","timezone":"UTC"}
      */
     @POST
     @Path("/")
@@ -96,6 +103,9 @@ public class CampaignsServiceEndPoint {
      *
      * @param query the query used to filter campaigns
      * @return metadata for campaigns that match the query
+     * @api.status 200 array org.apache.unomi.api.Metadata Matching campaign metadata (may be empty).
+     * @api.status 400 empty Invalid or unreadable query body.
+     * @api.example [{"id":"summer-sale","name":"Summer Sale","scope":"mysite","enabled":true}]
      */
     @POST
     @Path("/query")
@@ -105,9 +115,13 @@ public class CampaignsServiceEndPoint {
 
     /**
      * Returns detailed campaign data matching the given query.
+     * Each list element includes engagement metrics and the embedded {@link Campaign}.
      *
      * @param query the query specifying which campaigns to include
      * @return a paged list of campaign details
+     * @api.status 200 org.apache.unomi.api.PartialList Campaign details page (list items are CampaignDetail).
+     * @api.status 400 empty Invalid or unreadable query body.
+     * @api.example {"list":[{"engagedProfiles":120,"campaignSessionViews":450,"campaignSessionSuccess":38,"numberOfGoals":2,"conversionRate":0.084,"campaign":{"itemId":"summer-sale","itemType":"campaign","metadata":{"id":"summer-sale","name":"Summer Sale","scope":"mysite","enabled":true}}}],"offset":0,"pageSize":1,"totalSize":1,"totalSizeRelation":"EQUAL"}
      */
     @POST
     @Path("/query/detailed")
@@ -117,9 +131,12 @@ public class CampaignsServiceEndPoint {
 
     /**
      * Returns detailed data for the campaign with the given ID.
+     * When the campaign does not exist the endpoint returns {@code null} (HTTP 200 with empty body).
      *
      * @param campaignID the campaign identifier
      * @return the campaign detail, or {@code null} when no such campaign exists
+     * @api.status 200 org.apache.unomi.api.campaigns.CampaignDetail Campaign detail found, or empty body when missing.
+     * @api.example {"engagedProfiles":120,"campaignSessionViews":450,"campaignSessionSuccess":38,"numberOfGoals":2,"conversionRate":0.084,"campaign":{"itemId":"summer-sale","itemType":"campaign","metadata":{"id":"summer-sale","name":"Summer Sale","scope":"mysite","enabled":true}}}
      */
     @GET
     @Path("/{campaignID}/detailed")
@@ -129,9 +146,12 @@ public class CampaignsServiceEndPoint {
 
     /**
      * Returns the campaign definition for the given ID.
+     * When the campaign does not exist the endpoint returns {@code null} (HTTP 200 with empty body).
      *
      * @param campaignID the campaign identifier
      * @return the campaign, or {@code null} when no such campaign exists
+     * @api.status 200 org.apache.unomi.api.campaigns.Campaign Campaign found, or empty body when missing.
+     * @api.example {"itemId":"summer-sale","itemType":"campaign","metadata":{"id":"summer-sale","name":"Summer Sale","scope":"mysite","enabled":true},"startDate":"2024-06-01T00:00:00.000Z","endDate":"2024-08-31T23:59:59.000Z","entryCondition":{"type":"eventTypeCondition","parameterValues":{"eventTypeId":"view"}},"cost":5000.0,"currency":"USD","primaryGoal":"checkout-goal","timezone":"UTC"}
      */
     @GET
     @Path("/{campaignID}")
@@ -143,6 +163,8 @@ public class CampaignsServiceEndPoint {
      * Removes the campaign associated with the specified identifier, also removing associated rules if needed.
      *
      * @param campaignID the identifier of the campaign to be removed
+     * @api.status 204 empty Campaign deleted.
+     * @api.example {"itemId":"summer-sale","itemType":"campaign","metadata":{"id":"summer-sale","name":"Summer Sale","scope":"mysite","enabled":true},"startDate":"2024-06-01T00:00:00.000Z","endDate":"2024-08-31T23:59:59.000Z","entryCondition":{"type":"eventTypeCondition","parameterValues":{"eventTypeId":"view"}},"cost":5000.0,"currency":"USD","primaryGoal":"checkout-goal","timezone":"UTC"}
      */
     @DELETE
     @Path("/{campaignID}")
@@ -152,8 +174,12 @@ public class CampaignsServiceEndPoint {
 
     /**
      * Saves the specified campaign event in the context server.
+     * Body is a full {@link CampaignEvent}: {@code metadata}, {@code eventDate}, {@code campaignId}, and optional cost fields.
      *
      * @param campaignEvent the CampaignEvent to be saved
+     * @api.status 204 empty Campaign event created or updated.
+     * @api.status 400 empty Invalid or unreadable campaign event body.
+     * @api.example {"itemId":"summer-launch","itemType":"campaignevent","metadata":{"id":"summer-launch","name":"Campaign launch","scope":"mysite","enabled":true},"eventDate":"2024-06-01T09:00:00.000Z","campaignId":"summer-sale","cost":1000.0,"currency":"USD","timezone":"UTC"}
      */
     @POST
     @Path("/event")
@@ -165,6 +191,8 @@ public class CampaignsServiceEndPoint {
      * Removes the campaign event associated with the specified identifier.
      *
      * @param campaignEventID the identifier of the campaign event to be removed
+     * @api.status 204 empty Campaign event deleted.
+     * @api.example {"itemId":"summer-launch","itemType":"campaignevent","metadata":{"id":"summer-launch","name":"Campaign launch","scope":"mysite","enabled":true},"eventDate":"2024-06-01T09:00:00.000Z","campaignId":"summer-sale","cost":1000.0,"currency":"USD","timezone":"UTC"}
      */
     @DELETE
     @Path("/event/{eventId}")
@@ -177,6 +205,9 @@ public class CampaignsServiceEndPoint {
      *
      * @param query the query specifying which campaign events to include
      * @return a paged list of matching campaign events
+     * @api.status 200 org.apache.unomi.api.PartialList Campaign events page (list items are CampaignEvent).
+     * @api.status 400 empty Invalid or unreadable query body.
+     * @api.example {"list":[{"itemId":"summer-launch","itemType":"campaignevent","metadata":{"id":"summer-launch","name":"Campaign launch","scope":"mysite","enabled":true},"eventDate":"2024-06-01T09:00:00.000Z","campaignId":"summer-sale","cost":1000.0,"currency":"USD","timezone":"UTC"}],"offset":0,"pageSize":1,"totalSize":1,"totalSizeRelation":"EQUAL"}
      */
     @POST
     @Path("/events/query")
diff --git a/rest/src/main/java/org/apache/unomi/rest/endpoints/ClientEndpoint.java b/rest/src/main/java/org/apache/unomi/rest/endpoints/ClientEndpoint.java
index 2197b5c72..6fae685b0 100644
--- a/rest/src/main/java/org/apache/unomi/rest/endpoints/ClientEndpoint.java
+++ b/rest/src/main/java/org/apache/unomi/rest/endpoints/ClientEndpoint.java
@@ -73,6 +73,8 @@ public class ClientEndpoint {
      * Handles CORS preflight for the client endpoint.
      *
      * @return an empty CORS preflight response
+     * @api.status 204 empty CORS preflight accepted.
+     * @api.example {}
      */
     @OPTIONS
     @Path("/client/{operation}/{param}")
@@ -82,11 +84,18 @@ public class ClientEndpoint {
 
     /**
      * Exports profile data for the requested client operation.
+     * <p>
+     * Currently supports {@code myprofile} with a download format in {@code param}
+     * (must appear in the {@code allowedProfileDownloadFormats} configuration).
+     * Requires a profile id cookie; CSV layout can be vertical via the {@code vertical} query parameter.
      *
-     * @param operation the export operation
-     * @param param the operation parameter
-     * @return the export response
-     * @throws JsonProcessingException if response serialization fails
+     * @param operation the export operation (currently {@code myprofile})
+     * @param param the download format ({@code json}, {@code yaml}, {@code csv}, or {@code text})
+     * @return the export response with attachment headers
+     * @api.status 200 empty Profile export payload (JSON, YAML, CSV, or plain text depending on {@code param}).
+     * @api.status 404 empty Unknown operation, format, or missing profile cookie/profile.
+     * @api.status 500 empty {@code param} is not in {@code allowedProfileDownloadFormats}.
+     * @api.example {"itemId":"profile-42","itemType":"profile","properties":{"firstName":"Ada","email":"ada@example.com"},"segments":["vip"]}
      */
     @GET
     @Path("/client/{operation}/{param}")
diff --git a/rest/src/main/java/org/apache/unomi/rest/endpoints/ClusterServiceEndPoint.java b/rest/src/main/java/org/apache/unomi/rest/endpoints/ClusterServiceEndPoint.java
index 23fdc3232..05b3ee935 100644
--- a/rest/src/main/java/org/apache/unomi/rest/endpoints/ClusterServiceEndPoint.java
+++ b/rest/src/main/java/org/apache/unomi/rest/endpoints/ClusterServiceEndPoint.java
@@ -81,6 +81,8 @@ public class ClusterServiceEndPoint {
      * Returns cluster nodes known to this server instance.
      *
      * @return the available cluster nodes
+     * @api.status 200 array org.apache.unomi.api.ClusterNode Cluster nodes (may be empty).
+     * @api.example [{"itemId":"node-1","itemType":"clusterNode","publicHostAddress":"10.0.0.1","master":true,"data":true}]
      */
     @GET
     @Path("/")
@@ -90,8 +92,13 @@ public class ClusterServiceEndPoint {
 
     /**
      * Removes all data before the specified date from the context server.
+     * <p>
+     * The date must use {@code yyyy-MM-dd} format; unparseable values are logged and no purge is performed.
      *
-     * @param date the Date before which all data needs to be removed
+     * @param date the date before which all data is removed ({@code yyyy-MM-dd})
+     * @deprecated As of version 1.1.0-incubating, use scope-scoped purge instead
+     * @api.status 204 empty Purge requested when the date parsed successfully.
+     * @api.example {}
      */
     @GET
     @Path("/purge/{date}")
@@ -108,7 +115,9 @@ public class ClusterServiceEndPoint {
     /**
      * Removes all data associated with the provided scope.
      *
-     * @param scope the scope for which we want to remove data
+     * @param scope the scope for which data is removed
+     * @api.status 204 empty Scope data purge requested.
+     * @api.example {}
      */
     @DELETE
     @Path("{scope}")
diff --git a/rest/src/main/java/org/apache/unomi/rest/endpoints/ContextJsonEndpoint.java b/rest/src/main/java/org/apache/unomi/rest/endpoints/ContextJsonEndpoint.java
index 7543b8e35..c17076d26 100644
--- a/rest/src/main/java/org/apache/unomi/rest/endpoints/ContextJsonEndpoint.java
+++ b/rest/src/main/java/org/apache/unomi/rest/endpoints/ContextJsonEndpoint.java
@@ -53,6 +53,8 @@ import java.util.stream.Collectors;
 
 /**
  * REST endpoint for context.js and context.json requests.
+ * Public CDP entry points used by client SDKs to resolve profile/session context, ingest
+ * events, evaluate personalization filters, and return consented profile data.
  */
 @Consumes(MediaType.APPLICATION_JSON)
 @CrossOriginResourceSharing(allowAllOrigins = true, allowCredentials = true)
@@ -90,6 +92,8 @@ public class ContextJsonEndpoint {
      * Handles CORS preflight for context.js.
      *
      * @return an empty CORS preflight response
+     * @api.status 204 empty CORS preflight (no body).
+     * @api.example {}
      */
     @OPTIONS
     @Path("/context.js")
@@ -101,6 +105,8 @@ public class ContextJsonEndpoint {
      * Handles CORS preflight for context.json.
      *
      * @return an empty CORS preflight response
+     * @api.status 204 empty CORS preflight (no body).
+     * @api.example {}
      */
     @OPTIONS
     @Path("/context.json")
@@ -110,17 +116,23 @@ public class ContextJsonEndpoint {
 
     /**
      * Processes a context.js POST request.
+     * Same evaluation as {@code /context.json}, but returns {@code text/plain} JavaScript that
+     * assigns the {@link ContextResponse} JSON to {@code window.digitalData} / {@code cxs}.
      *
      * @param contextRequest the context request payload
-     * @param personaId optional persona identifier
-     * @param sessionId optional session identifier
-     * @param timestampAsLong optional request timestamp
-     * @param invalidateProfile whether to invalidate the profile
-     * @param invalidateSession whether to invalidate the session
-     * @param explain whether to include tracing details
+     * @param personaId optional persona identifier used to load a persona instead of a live profile
+     * @param sessionId optional session identifier (prefer body {@code sessionId} over the query string)
+     * @param timestampAsLong optional request time as epoch millis; defaults to server now
+     * @param invalidateProfile whether to drop and recreate the profile cookie/state
+     * @param invalidateSession whether to drop and recreate the session
+     * @param explain when {@code true}, include request tracing (requires administrator or tenant administrator)
      * @param securityContext the security context
-     * @return the context response wrapped as JavaScript
+     * @return JavaScript text wrapping a {@link ContextResponse}
      * @throws JsonProcessingException if response serialization fails
+     * @api.status 200 empty JavaScript that sets {@code cxs} from a ContextResponse JSON object.
+     * @api.status 400 empty Invalid personaId/sessionId schema or invalid request payload.
+     * @api.status 403 empty {@code explain=true} without administrator / tenant administrator role.
+     * @api.example window.digitalData=window.digitalData||{};var cxs={"profileId":"profile-42","sessionId":"session-1","profileProperties":{"firstName":"Ada"},"processedEvents":0,"anonymousBrowsing":false,"consents":{}};
      */
     @POST
     @Produces(MediaType.TEXT_PLAIN)
@@ -138,17 +150,23 @@ public class ContextJsonEndpoint {
 
     /**
      * Processes a context.js GET request.
+     * The {@link ContextRequest} is supplied as the {@code payload} query parameter (JSON).
+     * Prefer POST for production traffic; GET exists for legacy script tags.
      *
-     * @param contextRequest the context request payload
-     * @param personaId optional persona identifier
-     * @param sessionId optional session identifier
-     * @param timestampAsLong optional request timestamp
-     * @param invalidateProfile whether to invalidate the profile
-     * @param invalidateSession whether to invalidate the session
-     * @param explain whether to include tracing details
+     * @param contextRequest context request decoded from the {@code payload} query parameter
+     * @param personaId optional persona identifier used to load a persona instead of a live profile
+     * @param sessionId optional session identifier (prefer payload {@code sessionId} over the query string)
+     * @param timestampAsLong optional request time as epoch millis; defaults to server now
+     * @param invalidateProfile whether to drop and recreate the profile cookie/state
+     * @param invalidateSession whether to drop and recreate the session
+     * @param explain when {@code true}, include request tracing (requires administrator or tenant administrator)
      * @param securityContext the security context
-     * @return the context response wrapped as JavaScript
+     * @return JavaScript text wrapping a {@link ContextResponse}
      * @throws JsonProcessingException if response serialization fails
+     * @api.status 200 empty JavaScript that sets {@code cxs} from a ContextResponse JSON object.
+     * @api.status 400 empty Invalid personaId/sessionId schema or invalid payload.
+     * @api.status 403 empty {@code explain=true} without administrator / tenant administrator role.
+     * @api.example window.digitalData=window.digitalData||{};var cxs={"profileId":"profile-42","sessionId":"session-1","profileProperties":{"firstName":"Ada"},"processedEvents":0,"anonymousBrowsing":false,"consents":{}};
      */
     @GET
     @Produces(MediaType.TEXT_PLAIN)
@@ -172,16 +190,22 @@ public class ContextJsonEndpoint {
 
     /**
      * Processes a context.json GET request.
+     * The {@link ContextRequest} is supplied as the {@code payload} query parameter (JSON).
+     * Prefer POST for production traffic.
      *
-     * @param contextRequest the context request payload
-     * @param personaId optional persona identifier
-     * @param sessionId optional session identifier
-     * @param timestampAsLong optional request timestamp
-     * @param invalidateProfile whether to invalidate the profile
-     * @param invalidateSession whether to invalidate the session
-     * @param explain whether to include tracing details
+     * @param contextRequest context request decoded from the {@code payload} query parameter
+     * @param personaId optional persona identifier used to load a persona instead of a live profile
+     * @param sessionId optional session identifier (prefer payload {@code sessionId} over the query string)
+     * @param timestampAsLong optional request time as epoch millis; defaults to server now
+     * @param invalidateProfile whether to drop and recreate the profile cookie/state
+     * @param invalidateSession whether to drop and recreate the session
+     * @param explain when {@code true}, include {@code requestTracing} (requires administrator or tenant administrator)
      * @param securityContext the security context
      * @return the context response
+     * @api.status 200 org.apache.unomi.api.ContextResponse Context evaluation succeeded.
+     * @api.status 400 empty Invalid personaId/sessionId schema or invalid payload.
+     * @api.status 403 empty {@code explain=true} without administrator / tenant administrator role.
+     * @api.example {"profileId":"profile-1","sessionId":"session-1","profileProperties":{"firstName":"Ada"},"processedEvents":0,"anonymousBrowsing":false,"consents":{}}
      */
     @GET
     @Produces(MediaType.APPLICATION_JSON + ";charset=UTF-8")
@@ -198,17 +222,23 @@ public class ContextJsonEndpoint {
     }
 
     /**
-     * Processes a context.json POST request.
+     * Processes a context.json POST request (preferred public CDP entry point).
+     * Evaluates optional events, content filters, and personalizations; returns profile/session
+     * ids plus any requested properties, segments, scores, consents, and tracked conditions.
      *
-     * @param contextRequest the context request payload
-     * @param personaId optional persona identifier
-     * @param sessionId optional session identifier
-     * @param timestampAsLong optional request timestamp
-     * @param invalidateProfile whether to invalidate the profile
-     * @param invalidateSession whether to invalidate the session
-     * @param explain whether to include tracing details
+     * @param contextRequest the JSON context request body
+     * @param personaId optional persona identifier used to load a persona instead of a live profile
+     * @param sessionId optional session identifier (prefer body {@code sessionId} over the query string)
+     * @param timestampAsLong optional request time as epoch millis; defaults to server now
+     * @param invalidateProfile whether to drop and recreate the profile cookie/state
+     * @param invalidateSession whether to drop and recreate the session
+     * @param explain when {@code true}, include {@code requestTracing} (requires administrator or tenant administrator)
      * @param securityContext the security context
      * @return the context response
+     * @api.status 200 org.apache.unomi.api.ContextResponse Context evaluation succeeded.
+     * @api.status 400 empty Invalid personaId/sessionId schema or invalid request body.
+     * @api.status 403 empty {@code explain=true} without administrator / tenant administrator role.
+     * @api.example {"profileId":"profile-1","sessionId":"session-1","profileProperties":{"firstName":"Ada"},"profileSegments":["vip"],"processedEvents":1,"anonymousBrowsing":false,"consents":{}}
      */
     @POST
     @Produces(MediaType.APPLICATION_JSON + ";charset=UTF-8")
diff --git a/rest/src/main/java/org/apache/unomi/rest/endpoints/DefinitionsServiceEndPoint.java b/rest/src/main/java/org/apache/unomi/rest/endpoints/DefinitionsServiceEndPoint.java
index 2a2c3c62d..42522a745 100644
--- a/rest/src/main/java/org/apache/unomi/rest/endpoints/DefinitionsServiceEndPoint.java
+++ b/rest/src/main/java/org/apache/unomi/rest/endpoints/DefinitionsServiceEndPoint.java
@@ -72,9 +72,13 @@ public class DefinitionsServiceEndPoint {
 
     /**
      * Returns all condition types localized for the requested language.
+     * <p>
+     * Labels and descriptions follow the {@code Accept-Language} header when provided.
      *
-     * @param language the locale to use for labels and descriptions
+     * @param language the locale to use for labels and descriptions (from {@code Accept-Language})
      * @return all condition types in REST form
+     * @api.status 200 array org.apache.unomi.rest.models.RESTConditionType All condition types (may be empty).
+     * @api.example [{"id":"profilePropertyCondition","name":"Profile property","description":"Checks a profile property value","tags":["profile"],"systemTags":["profile"],"parameters":[{"id":"propertyName","type":"string"},{"id":"comparisonOperator","type":"string"},{"id":"propertyValue","type":"string"}],"version":1}]
      */
     @GET
     @Path("/conditions")
@@ -85,10 +89,15 @@ public class DefinitionsServiceEndPoint {
 
     /**
      * Returns condition types that match any of the given tags.
+     * <p>
+     * The {@code tags} path segment is a comma-separated list; each tag is matched independently
+     * and results are de-duplicated.
      *
-     * @param language the locale to use for labels and descriptions
      * @param tags a comma-separated list of tag identifiers
+     * @param language the locale to use for labels and descriptions (from {@code Accept-Language})
      * @return matching condition types in REST form
+     * @api.status 200 array org.apache.unomi.rest.models.RESTConditionType Matching condition types (may be empty).
+     * @api.example [{"id":"eventTypeCondition","name":"Event type","description":"Matches events of a given type","tags":["event"],"systemTags":["event"],"parameters":[{"id":"eventTypeId","type":"string"}],"version":1}]
      */
     @GET
     @Path("/conditions/tags/{tags}")
@@ -103,10 +112,15 @@ public class DefinitionsServiceEndPoint {
 
     /**
      * Returns condition types that match any of the given system tags.
+     * <p>
+     * The {@code tags} path segment is a comma-separated list; each system tag is matched
+     * independently and results are de-duplicated.
      *
-     * @param language the locale to use for labels and descriptions
      * @param tags a comma-separated list of system tag identifiers
+     * @param language the locale to use for labels and descriptions (from {@code Accept-Language})
      * @return matching condition types in REST form
+     * @api.status 200 array org.apache.unomi.rest.models.RESTConditionType Matching condition types (may be empty).
+     * @api.example [{"id":"profilePropertyCondition","name":"Profile property","description":"Checks a profile property value","tags":["profile"],"systemTags":["profile"],"parameters":[{"id":"propertyName","type":"string"},{"id":"comparisonOperator","type":"string"},{"id":"propertyValue","type":"string"}],"version":1}]
      */
     @GET
     @Path("/conditions/systemTags/{tags}")
@@ -121,10 +135,13 @@ public class DefinitionsServiceEndPoint {
 
     /**
      * Returns the condition type with the given ID, localized for the requested language.
+     * When the condition type does not exist the endpoint returns {@code null} (HTTP 200 with empty body).
      *
-     * @param language the locale to use for labels and descriptions
      * @param id the condition type identifier
+     * @param language the locale to use for labels and descriptions (from {@code Accept-Language})
      * @return the condition type in REST form, or {@code null} when it does not exist
+     * @api.status 200 org.apache.unomi.rest.models.RESTConditionType Condition type found, or empty body when missing.
+     * @api.example {"id":"profilePropertyCondition","name":"Profile property","description":"Checks a profile property value","tags":["profile"],"systemTags":["profile"],"parameters":[{"id":"propertyName","type":"string"},{"id":"comparisonOperator","type":"string"},{"id":"propertyValue","type":"string"}],"version":1}
      */
     @GET
     @Path("/conditions/{conditionId}")
@@ -135,8 +152,12 @@ public class DefinitionsServiceEndPoint {
 
     /**
      * Stores the given condition type definition.
+     * Body is a full {@link ConditionType}: {@code metadata}, evaluator metadata, and {@code parameters}.
      *
      * @param conditionType the condition type to store
+     * @api.status 204 empty Condition type created or updated.
+     * @api.status 400 empty Invalid or unreadable condition type body.
+     * @api.example {"itemId":"profilePropertyCondition","itemType":"conditionType","metadata":{"id":"profilePropertyCondition","name":"Profile property","scope":"systemscope","enabled":true},"conditionEvaluator":"profilePropertyConditionEvaluator","parameters":[{"id":"propertyName","type":"string"},{"id":"comparisonOperator","type":"string"},{"id":"propertyValue","type":"string"}]}
      */
     @POST
     @Path("/conditions")
@@ -148,6 +169,8 @@ public class DefinitionsServiceEndPoint {
      * Deletes the condition type with the given ID.
      *
      * @param conditionTypeId the condition type identifier
+     * @api.status 204 empty Condition type deleted.
+     * @api.example {"itemId":"profilePropertyCondition","itemType":"conditionType","metadata":{"id":"profilePropertyCondition","name":"Profile property","scope":"systemscope","enabled":true},"conditionEvaluator":"profilePropertyConditionEvaluator","parameters":[{"id":"propertyName","type":"string"},{"id":"comparisonOperator","type":"string"},{"id":"propertyValue","type":"string"}]}
      */
     @DELETE
     @Path("/conditions/{conditionTypeId}")
@@ -157,9 +180,13 @@ public class DefinitionsServiceEndPoint {
 
     /**
      * Returns all action types localized for the requested language.
+     * <p>
+     * Labels and descriptions follow the {@code Accept-Language} header when provided.
      *
-     * @param language the locale to use for labels and descriptions
+     * @param language the locale to use for labels and descriptions (from {@code Accept-Language})
      * @return all action types in REST form
+     * @api.status 200 array org.apache.unomi.rest.models.RESTActionType All action types (may be empty).
+     * @api.example [{"id":"setPropertyAction","name":"Set property","description":"Sets a profile or session property","tags":["profile"],"systemTags":["profile"],"parameters":[{"id":"setPropertyName","type":"string"},{"id":"setPropertyValueBoolean","type":"boolean"}],"version":1}]
      */
     @GET
     @Path("/actions")
@@ -170,10 +197,15 @@ public class DefinitionsServiceEndPoint {
 
     /**
      * Returns action types that match any of the given tags.
+     * <p>
+     * The {@code tags} path segment is a comma-separated list; each tag is matched independently
+     * and results are de-duplicated.
      *
-     * @param language the locale to use for labels and descriptions
      * @param tags a comma-separated list of tag identifiers
+     * @param language the locale to use for labels and descriptions (from {@code Accept-Language})
      * @return matching action types in REST form
+     * @api.status 200 array org.apache.unomi.rest.models.RESTActionType Matching action types (may be empty).
+     * @api.example [{"id":"setPropertyAction","name":"Set property","description":"Sets a profile or session property","tags":["profile"],"systemTags":["profile"],"parameters":[{"id":"setPropertyName","type":"string"},{"id":"setPropertyValueBoolean","type":"boolean"}],"version":1}]
      */
     @GET
     @Path("/actions/tags/{tags}")
@@ -188,10 +220,15 @@ public class DefinitionsServiceEndPoint {
 
     /**
      * Returns action types that match any of the given system tags.
+     * <p>
+     * The {@code tags} path segment is a comma-separated list; each system tag is matched
+     * independently and results are de-duplicated.
      *
-     * @param language the locale to use for labels and descriptions
      * @param tags a comma-separated list of system tag identifiers
+     * @param language the locale to use for labels and descriptions (from {@code Accept-Language})
      * @return matching action types in REST form
+     * @api.status 200 array org.apache.unomi.rest.models.RESTActionType Matching action types (may be empty).
+     * @api.example [{"id":"setPropertyAction","name":"Set property","description":"Sets a profile or session property","tags":["profile"],"systemTags":["profile"],"parameters":[{"id":"setPropertyName","type":"string"},{"id":"setPropertyValueBoolean","type":"boolean"}],"version":1}]
      */
     @GET
     @Path("/actions/systemTags/{tags}")
@@ -206,10 +243,13 @@ public class DefinitionsServiceEndPoint {
 
     /**
      * Returns the action type with the given ID, localized for the requested language.
+     * When the action type does not exist the endpoint returns {@code null} (HTTP 200 with empty body).
      *
-     * @param language the locale to use for labels and descriptions
      * @param id the action type identifier
+     * @param language the locale to use for labels and descriptions (from {@code Accept-Language})
      * @return the action type in REST form, or {@code null} when it does not exist
+     * @api.status 200 org.apache.unomi.rest.models.RESTActionType Action type found, or empty body when missing.
+     * @api.example {"id":"setPropertyAction","name":"Set property","description":"Sets a profile or session property","tags":["profile"],"systemTags":["profile"],"parameters":[{"id":"setPropertyName","type":"string"},{"id":"setPropertyValueBoolean","type":"boolean"}],"version":1}
      */
     @GET
     @Path("/actions/{actionId}")
@@ -220,8 +260,12 @@ public class DefinitionsServiceEndPoint {
 
     /**
      * Stores the given action type definition.
+     * Body is a full {@link ActionType}: {@code metadata}, executor metadata, and {@code parameters}.
      *
      * @param actionType the action type to store
+     * @api.status 204 empty Action type created or updated.
+     * @api.status 400 empty Invalid or unreadable action type body.
+     * @api.example {"itemId":"setPropertyAction","itemType":"actionType","metadata":{"id":"setPropertyAction","name":"Set property","scope":"systemscope","enabled":true},"actionExecutor":"setPropertyActionExecutor","parameters":[{"id":"setPropertyName","type":"string"},{"id":"setPropertyValueBoolean","type":"boolean"}]}
      */
     @POST
     @Path("/actions")
@@ -233,6 +277,8 @@ public class DefinitionsServiceEndPoint {
      * Deletes the action type with the given ID.
      *
      * @param actionTypeId the action type identifier
+     * @api.status 204 empty Action type deleted.
+     * @api.example {"itemId":"setPropertyAction","itemType":"actionType","metadata":{"id":"setPropertyAction","name":"Set property","scope":"systemscope","enabled":true},"actionExecutor":"setPropertyActionExecutor","parameters":[{"id":"setPropertyName","type":"string"},{"id":"setPropertyValueBoolean","type":"boolean"}]}
      */
     @DELETE
     @Path("/actions/{actionTypeId}")
@@ -242,9 +288,13 @@ public class DefinitionsServiceEndPoint {
 
     /**
      * Returns all value types localized for the requested language.
+     * <p>
+     * Labels and descriptions follow the {@code Accept-Language} header when provided.
      *
-     * @param language the locale to use for labels and descriptions
+     * @param language the locale to use for labels and descriptions (from {@code Accept-Language})
      * @return all value types in REST form
+     * @api.status 200 array org.apache.unomi.rest.models.RESTValueType All value types (may be empty).
+     * @api.example [{"id":"string","name":"String","description":"Text value","tags":["primitive"]}]
      */
     @GET
     @Path("/values")
@@ -254,10 +304,15 @@ public class DefinitionsServiceEndPoint {
 
     /**
      * Returns value types that match any of the given tags.
+     * <p>
+     * The {@code tags} path segment is a comma-separated list; each tag is matched independently
+     * and results are de-duplicated.
      *
-     * @param language the locale to use for labels and descriptions
      * @param tags a comma-separated list of tag identifiers
+     * @param language the locale to use for labels and descriptions (from {@code Accept-Language})
      * @return matching value types in REST form
+     * @api.status 200 array org.apache.unomi.rest.models.RESTValueType Matching value types (may be empty).
+     * @api.example [{"id":"string","name":"String","description":"Text value","tags":["primitive"]}]
      */
     @GET
     @Path("/values/tags/{tags}")
@@ -273,9 +328,12 @@ public class DefinitionsServiceEndPoint {
     /**
      * Returns the value type with the given ID, localized for the requested language.
      *
-     * @param language the locale to use for labels and descriptions
      * @param id the value type identifier
-     * @return the value type in REST form, or {@code null} when it does not exist
+     * @param language the locale to use for labels and descriptions (from {@code Accept-Language})
+     * @return the value type in REST form
+     * @api.status 200 org.apache.unomi.rest.models.RESTValueType Value type found.
+     * @api.status 404 empty Value type not found.
+     * @api.example {"id":"string","name":"String","description":"Text value","tags":["primitive"]}
      */
     @GET
     @Path("/values/{valueTypeId}")
@@ -289,8 +347,12 @@ public class DefinitionsServiceEndPoint {
 
     /**
      * Returns plugin types grouped by plugin identifier.
+     * <p>
+     * Keys are numeric plugin IDs; values are lists of condition and action types contributed by each plugin.
      *
      * @return plugin ID to plugin type list mappings
+     * @api.status 200 empty Map of plugin id to condition/action type lists (may be empty).
+     * @api.example {"42":[{"itemId":"profilePropertyCondition","itemType":"conditionType"}]}
      */
     @GET
     @Path("/typesByPlugin")
diff --git a/rest/src/main/java/org/apache/unomi/rest/endpoints/EventServiceEndpoint.java b/rest/src/main/java/org/apache/unomi/rest/endpoints/EventServiceEndpoint.java
index e3497922d..bff09b231 100644
--- a/rest/src/main/java/org/apache/unomi/rest/endpoints/EventServiceEndpoint.java
+++ b/rest/src/main/java/org/apache/unomi/rest/endpoints/EventServiceEndpoint.java
@@ -55,9 +55,13 @@ public class EventServiceEndpoint {
 
     /**
      * Searches events using the given query.
+     * The query may include a condition tree ({@code type} + {@code parameterValues}), offset, and limit.
      *
      * @param query the search query, including optional condition tree, offset, and limit
      * @return a paged list of matching events
+     * @api.status 200 org.apache.unomi.api.PartialList Events page (list items are Event).
+     * @api.status 400 empty Invalid or unreadable query body.
+     * @api.example {"list":[{"itemId":"evt-1","itemType":"event","eventType":"view","sessionId":"session-1","profileId":"profile-1","scope":"mysite","timeStamp":"2024-06-15T10:30:00.000Z","properties":{"pageUrl":"https://example.com/"},"persistent":true}],"offset":0,"pageSize":1,"totalSize":1,"totalSizeRelation":"EQUAL"}
      */
     @POST
     @Path("/search")
@@ -69,7 +73,10 @@ public class EventServiceEndpoint {
      * Returns the event with the given ID.
      *
      * @param id the event identifier
-     * @return the event, or {@code null} when it does not exist
+     * @return the event, or HTTP 404 when it does not exist
+     * @api.status 200 org.apache.unomi.api.Event Event found.
+     * @api.status 404 empty Event not found.
+     * @api.example {"itemId":"evt-1","itemType":"event","eventType":"view","sessionId":"session-1","profileId":"profile-1","scope":"mysite","timeStamp":"2024-06-15T10:30:00.000Z","properties":{"pageUrl":"https://example.com/"},"persistent":true}
      */
     @GET
     @Path("/{id}")
@@ -85,6 +92,8 @@ public class EventServiceEndpoint {
      * Deletes an event by id.
      *
      * @param id the identifier for the event to delete
+     * @api.status 204 empty Event deleted.
+     * @api.example {"itemId":"evt-1","itemType":"event","eventType":"view","sessionId":"session-1","profileId":"profile-42","scope":"mysite","timeStamp":"2024-06-15T10:30:00.000Z","properties":{"pageUrl":"https://example.com/"},"persistent":true}
      */
     @DELETE
     @Path("/{id}")
@@ -96,6 +105,8 @@ public class EventServiceEndpoint {
      * Returns event type identifiers known to the server.
      *
      * @return the processed event type identifiers
+     * @api.status 200 array empty Event type ids known to the server (may be empty).
+     * @api.example ["view","login","purchase"]
      */
     @GET
     @Path("types")
diff --git a/rest/src/main/java/org/apache/unomi/rest/endpoints/EventsCollectorEndpoint.java b/rest/src/main/java/org/apache/unomi/rest/endpoints/EventsCollectorEndpoint.java
index 1cfa8a1fe..48e7e5c7f 100644
--- a/rest/src/main/java/org/apache/unomi/rest/endpoints/EventsCollectorEndpoint.java
+++ b/rest/src/main/java/org/apache/unomi/rest/endpoints/EventsCollectorEndpoint.java
@@ -43,7 +43,9 @@ import java.util.Date;
 import java.util.List;
 
 /**
- * REST endpoint for collecting client events.
+ * REST endpoint for collecting client events ({@code /eventcollector}).
+ * Accepts a batch of {@link org.apache.unomi.api.Event} objects, attaches them to a profile/session,
+ * runs rules, and returns a bitwise {@code updated} change mask.
  */
 @Produces(MediaType.APPLICATION_JSON + ";charset=UTF-8")
 @Consumes(MediaType.APPLICATION_JSON)
@@ -70,6 +72,8 @@ public class EventsCollectorEndpoint {
      * Handles CORS preflight for the event collector endpoint.
      *
      * @return an empty CORS preflight response
+     * @api.status 204 empty CORS preflight (no body).
+     * @api.example {}
      */
     @OPTIONS
     @Path("/eventcollector")
@@ -79,12 +83,18 @@ public class EventsCollectorEndpoint {
 
     /**
      * Collects events from a GET request.
+     * The {@link EventsCollectorRequest} is supplied as the {@code payload} query parameter (JSON).
+     * Prefer POST for production traffic.
      *
-     * @param eventsCollectorRequest the events collector request
-     * @param timestampAsString optional request timestamp
-     * @param explain whether to include tracing details
+     * @param eventsCollectorRequest collector request decoded from the {@code payload} query parameter
+     * @param timestampAsString optional request time as epoch millis; defaults to server now
+     * @param explain when {@code true}, include {@code requestTracing} (requires administrator or tenant administrator)
      * @param securityContext the security context
      * @return the event collector response
+     * @api.status 200 org.apache.unomi.rest.models.EventCollectorResponse Events processed; see {@code updated} bitwise flags.
+     * @api.status 400 empty Missing/empty collector payload or invalid event data.
+     * @api.status 403 empty {@code explain=true} without administrator / tenant administrator role.
+     * @api.example {"updated":6}
      */
     @GET
     @Path("/eventcollector")
@@ -96,13 +106,18 @@ public class EventsCollectorEndpoint {
     }
 
     /**
-     * Collects events from a POST request.
+     * Collects events from a POST request (preferred).
+     * Submits an {@link org.apache.unomi.api.EventsCollectorRequest} body so Unomi can attach events to a session/profile, run rules, and return bitwise update flags.
      *
-     * @param eventsCollectorRequest the events collector request
-     * @param timestampAsLong optional request timestamp
-     * @param explain whether to include tracing details
+     * @param eventsCollectorRequest JSON body with {@code events} plus optional {@code sessionId}/{@code profileId}/{@code publicApiKey}
+     * @param timestampAsLong optional request time as epoch millis; defaults to server now
+     * @param explain when {@code true}, include {@code requestTracing} (requires administrator or tenant administrator)
      * @param securityContext the security context
      * @return the event collector response
+     * @api.status 200 org.apache.unomi.rest.models.EventCollectorResponse Events processed; see {@code updated} bitwise flags.
+     * @api.status 400 empty Missing/empty collector body or invalid event data.
+     * @api.status 403 empty {@code explain=true} without administrator / tenant administrator role.
+     * @api.example {"updated":6}
      */
     @POST
     @Path("/eventcollector")
diff --git a/rest/src/main/java/org/apache/unomi/rest/endpoints/GoalsServiceEndPoint.java b/rest/src/main/java/org/apache/unomi/rest/endpoints/GoalsServiceEndPoint.java
index 2236ba876..f5a3c15b1 100644
--- a/rest/src/main/java/org/apache/unomi/rest/endpoints/GoalsServiceEndPoint.java
+++ b/rest/src/main/java/org/apache/unomi/rest/endpoints/GoalsServiceEndPoint.java
@@ -61,6 +61,8 @@ public class GoalsServiceEndPoint {
      * Returns metadata for all goals.
      *
      * @return goal metadata for every stored goal
+     * @api.status 200 array org.apache.unomi.api.Metadata Goal metadata for all stored goals (may be empty).
+     * @api.example [{"id":"checkout-goal","name":"Checkout completed","scope":"mysite","enabled":true}]
      */
     @GET
     @Path("/")
@@ -70,8 +72,13 @@ public class GoalsServiceEndPoint {
 
     /**
      * Saves the specified goal in the context server and creates associated {@link Rule}s if the goal is enabled.
+     * Body is a full {@link Goal}: {@code metadata}, optional {@code startEvent} and {@code targetEvent}
+     * (each a condition with JSON field {@code type} + {@code parameterValues}), and optional {@code campaignId}.
      *
      * @param goal the Goal to be saved
+     * @api.status 204 empty Goal created or updated.
+     * @api.status 400 empty Invalid or unreadable goal body.
+     * @api.example {"itemId":"checkout-goal","itemType":"goal","metadata":{"id":"checkout-goal","name":"Checkout completed","scope":"mysite","enabled":true},"startEvent":{"type":"eventTypeCondition","parameterValues":{"eventTypeId":"view"}},"targetEvent":{"type":"eventTypeCondition","parameterValues":{"eventTypeId":"purchase"}}}
      */
     @POST
     @Path("/")
@@ -84,6 +91,9 @@ public class GoalsServiceEndPoint {
      *
      * @param query the query used to filter goals
      * @return metadata for goals that match the query
+     * @api.status 200 array org.apache.unomi.api.Metadata Matching goal metadata (may be empty).
+     * @api.status 400 empty Invalid or unreadable query body.
+     * @api.example [{"id":"checkout-goal","name":"Checkout completed","scope":"mysite","enabled":true}]
      */
     @POST
     @Path("/query")
@@ -93,9 +103,12 @@ public class GoalsServiceEndPoint {
 
     /**
      * Returns the goal with the given ID.
+     * When the goal does not exist the endpoint returns {@code null} (HTTP 200 with empty body).
      *
      * @param goalId the goal identifier
      * @return the goal, or {@code null} when it does not exist
+     * @api.status 200 org.apache.unomi.api.goals.Goal Goal found, or empty body when missing.
+     * @api.example {"itemId":"checkout-goal","itemType":"goal","metadata":{"id":"checkout-goal","name":"Checkout completed","scope":"mysite","enabled":true},"startEvent":{"type":"eventTypeCondition","parameterValues":{"eventTypeId":"view"}},"targetEvent":{"type":"eventTypeCondition","parameterValues":{"eventTypeId":"purchase"}}}
      */
     @GET
     @Path("/{goalId}")
@@ -107,6 +120,8 @@ public class GoalsServiceEndPoint {
      * Removes the goal associated with the specified identifier, also removing associated rules if needed.
      *
      * @param goalId the identifier of the goal to be removed
+     * @api.status 204 empty Goal deleted.
+     * @api.example {"itemId":"checkout-goal","itemType":"goal","metadata":{"id":"checkout-goal","name":"Checkout completed","scope":"mysite","enabled":true},"startEvent":{"type":"eventTypeCondition","parameterValues":{"eventTypeId":"view"}},"targetEvent":{"type":"eventTypeCondition","parameterValues":{"eventTypeId":"purchase"}}}
      */
     @DELETE
     @Path("/{goalId}")
@@ -119,6 +134,9 @@ public class GoalsServiceEndPoint {
      *
      * @param goalId the goal identifier
      * @return the goal report
+     * @api.status 200 org.apache.unomi.api.goals.GoalReport Goal report with global and split statistics.
+     * @api.status 404 empty Goal not found.
+     * @api.example {"globalStats":{"key":"global","startCount":1000,"targetCount":120,"conversionRate":0.12,"percentage":100.0},"split":[{"key":"variant-a","startCount":500,"targetCount":70,"conversionRate":0.14,"percentage":50.0}]}
      */
     @GET
     @Path("/{goalID}/report")
@@ -131,10 +149,15 @@ public class GoalsServiceEndPoint {
 
     /**
      * Returns a filtered goal report for the given goal and aggregate query.
+     * The aggregate query limits which report elements are included in the response.
      *
      * @param goalId the goal identifier
      * @param query the aggregate query that limits report elements
      * @return the filtered goal report
+     * @api.status 200 org.apache.unomi.api.goals.GoalReport Filtered goal report.
+     * @api.status 400 empty Invalid or unreadable aggregate query body.
+     * @api.status 404 empty Goal not found.
+     * @api.example {"globalStats":{"key":"global","startCount":1000,"targetCount":120,"conversionRate":0.12,"percentage":100.0},"split":[{"key":"variant-a","startCount":500,"targetCount":70,"conversionRate":0.14,"percentage":50.0}]}
      */
     @POST
     @Path("/{goalID}/report")
diff --git a/rest/src/main/java/org/apache/unomi/rest/endpoints/PatchServiceEndPoint.java b/rest/src/main/java/org/apache/unomi/rest/endpoints/PatchServiceEndPoint.java
index 19fe51fb4..00429f82e 100644
--- a/rest/src/main/java/org/apache/unomi/rest/endpoints/PatchServiceEndPoint.java
+++ b/rest/src/main/java/org/apache/unomi/rest/endpoints/PatchServiceEndPoint.java
@@ -52,10 +52,14 @@ public class PatchServiceEndPoint {
     }
 
     /**
-     * Apply a patch on an item
+     * Applies a patch to an item.
+     * <p>
+     * When {@code force} is {@code false} or omitted and the patch was already applied, the call is a no-op.
      *
      * @param patch the patch to apply
-     * @param force a boolean to force (or not) the application of the patch even if it was previously applied.
+     * @param force when {@code true}, re-applies even if previously applied
+     * @api.status 204 empty Patch applied or skipped because it was already applied.
+     * @api.example {"itemId":"profile-1","itemType":"patch","patches":[{"operation":"set","path":"properties.firstName","value":"Ada"}]}
      */
     @POST
     @Path("/apply")
diff --git a/rest/src/main/java/org/apache/unomi/rest/endpoints/ProfileServiceEndPoint.java b/rest/src/main/java/org/apache/unomi/rest/endpoints/ProfileServiceEndPoint.java
index b03208e87..3c43745f9 100644
--- a/rest/src/main/java/org/apache/unomi/rest/endpoints/ProfileServiceEndPoint.java
+++ b/rest/src/main/java/org/apache/unomi/rest/endpoints/ProfileServiceEndPoint.java
@@ -116,6 +116,8 @@ public class ProfileServiceEndPoint {
      * Returns the total number of unique profiles.
      *
      * @return the profile count
+     * @api.status 200 empty Total number of unique profiles.
+     * @api.example 1200
      */
     @GET
     @Path("/count")
@@ -125,9 +127,13 @@ public class ProfileServiceEndPoint {
 
     /**
      * Returns profiles matching the given query.
+     * Prefer a {@link org.apache.unomi.api.conditions.Condition} in the body for precise filters; optional full-text {@code text} and paging fields are also supported.
      *
      * @param query the search query
      * @return a paged list of matching profiles
+     * @api.status 200 org.apache.unomi.api.PartialList Profiles page (list items are Profile).
+     * @api.status 400 empty Invalid or unreadable query body.
+     * @api.example {"list":[{"itemId":"profile-1","itemType":"profile","properties":{"firstName":"Ada","email":"ada@example.com"},"segments":["vip"]}],"offset":0,"pageSize":1,"totalSize":1,"totalSizeRelation":"EQUAL"}
      */
     @POST
     @Path("/search")
@@ -137,9 +143,14 @@ public class ProfileServiceEndPoint {
 
     /**
      * Exports matching profiles as a downloadable CSV file.
+     * The {@code query} parameter must be a JSON-encoded {@link org.apache.unomi.api.query.Query}.
      *
      * @param query JSON query string describing which profiles to export
      * @return a CSV download response
+     * @api.status 200 empty CSV attachment with matching profile properties.
+     * @api.status 400 empty {@code query} JSON is missing or not a valid Query.
+     * @api.status 500 empty Export failed while parsing the query or generating CSV.
+     * @api.example itemId,properties.email,properties.firstName\nprofile-42,ada@example.com,Ada
      */
     @GET
     @Path("/export")
@@ -158,6 +169,9 @@ public class ProfileServiceEndPoint {
      *
      * @param query a form-encoded representation of the query the profiles to export should match
      * @return a Response object configured to allow caller to download the CSV export file
+     * @api.status 200 empty CSV attachment with matching profile properties.
+     * @api.status 400 empty Form {@code query} is missing or not a valid Query JSON.
+     * @api.status 500 empty Export failed while parsing the query or generating CSV.
      */
     @GET
     @Path("/export")
@@ -177,6 +191,9 @@ public class ProfileServiceEndPoint {
      *
      * @param query the query describing which profiles to export
      * @return a CSV download response
+     * @api.status 200 empty CSV attachment with matching profile properties.
+     * @api.status 400 empty Invalid or unreadable query body.
+     * @api.example itemId,properties.email,properties.firstName\nprofile-42,ada@example.com,Ada
      */
     @POST
     @Path("/export")
@@ -191,8 +208,12 @@ public class ProfileServiceEndPoint {
 
     /**
      * Applies a batch update to all matching profiles.
+     * Selects matching profiles with {@link org.apache.unomi.api.BatchUpdate#getCondition()} and sets {@code propertyName} to {@code propertyValue}.
      *
      * @param update the batch update specification
+     * @api.status 204 empty Batch update accepted and processed.
+     * @api.status 400 empty Invalid batch update body (missing propertyName/condition).
+     * @api.example {"propertyName":"properties.email","propertyValue":"updated@example.com","condition":{"type":"profilePropertyCondition","parameterValues":{"propertyName":"properties.email","comparisonOperator":"exists","propertyValue":""}},"strategy":"defaultPropertyMergeStrategy","scrollTimeValidity":"10m","scrollBatchSize":1000}
      */
     @POST
     @Path("/batchProfilesUpdate")
@@ -202,9 +223,12 @@ public class ProfileServiceEndPoint {
 
     /**
      * Returns the profile with the given ID.
+     * When the profile does not exist the endpoint returns {@code null} (HTTP 200 with empty body), not 404.
      *
      * @param profileId the profile identifier
      * @return the profile, or {@code null} when it does not exist
+     * @api.status 200 org.apache.unomi.api.Profile Profile found, or empty body when missing.
+     * @api.example {"itemId":"profile-1","itemType":"profile","properties":{"firstName":"Ada","email":"ada@example.com"},"segments":["vip"],"scores":{"engagement":12},"consents":{}}
      */
     @GET
     @Path("/{profileId}")
@@ -214,9 +238,13 @@ public class ProfileServiceEndPoint {
 
     /**
      * Saves the specified profile in the context server, sending a {@code profileUpdated} event.
+     * Creates a new profile or merges into an existing one when applicable, then fires a non-persistent {@code profileUpdated} event.
      *
      * @param profile the profile to be saved
      * @return the newly saved profile
+     * @api.status 200 org.apache.unomi.api.Profile Profile saved (or merged).
+     * @api.status 400 empty Invalid or unreadable profile body.
+     * @api.example {"itemId":"profile-1","itemType":"profile","properties":{"firstName":"Ada","email":"ada@example.com"},"segments":["vip"]}
      */
     @POST
     @Path("/")
@@ -238,6 +266,8 @@ public class ProfileServiceEndPoint {
      *
      * @param profileId the identifier of the profile or persona to delete
      * @param persona   {@code true} if the specified identifier is supposed to refer to a persona, {@code false} if it is supposed to refer to a profile
+     * @api.status 204 empty Profile or persona deleted.
+     * @api.example {"itemId":"profile-42","itemType":"profile","properties":{"firstName":"Ada","email":"ada@example.com"}}
      */
     @DELETE
     @Path("/{profileId}")
@@ -256,6 +286,8 @@ public class ProfileServiceEndPoint {
      * @param size maximum number of results to return, or {@code -1} for all matches
      * @param sortBy optional comma-separated sort fields with optional {@code :asc} or {@code :desc}
      * @return a paged list of matching sessions
+     * @api.status 200 org.apache.unomi.api.PartialList Sessions page (list items are Session).
+     * @api.example {"list":[{"itemId":"session-1","itemType":"session","profileId":"profile-1","scope":"mysite","size":4}],"offset":0,"pageSize":1,"totalSize":1,"totalSizeRelation":"EQUAL"}
      */
     @GET
     @Path("/{profileId}/sessions")
@@ -272,6 +304,8 @@ public class ProfileServiceEndPoint {
      *
      * @param profileId the profile identifier
      * @return segment metadata for memberships of this profile
+     * @api.status 200 array org.apache.unomi.api.Metadata Segment memberships for the profile (may be empty).
+     * @api.example [{"id":"vip","name":"VIP customers","scope":"mysite","enabled":true}]
      */
     @GET
     @Path("/{profileId}/segments")
@@ -285,6 +319,8 @@ public class ProfileServiceEndPoint {
      *
      * @param fromPropertyTypeId the source property type identifier
      * @return the mapped target property type identifier
+     * @api.status 200 empty Mapped target property type id (may be empty/null when unmapped).
+     * @api.example email
      */
     @GET
     @Path("/properties/mappings/{fromPropertyTypeId}")
@@ -297,6 +333,9 @@ public class ProfileServiceEndPoint {
      *
      * @param query the search query
      * @return a paged list of matching personas
+     * @api.status 200 org.apache.unomi.api.PartialList Personas page (list items are Persona).
+     * @api.status 400 empty Invalid or unreadable query body.
+     * @api.example {"list":[{"itemId":"persona-vip","itemType":"persona","properties":{"firstName":"VIP"}}],"offset":0,"pageSize":1,"totalSize":1,"totalSizeRelation":"EQUAL"}
      */
     @POST
     @Path("/personas/search")
@@ -306,9 +345,12 @@ public class ProfileServiceEndPoint {
 
     /**
      * Returns the persona with the given ID.
+     * When the persona does not exist the endpoint returns {@code null} (HTTP 200 with empty body).
      *
      * @param personaId the persona identifier
      * @return the persona, or {@code null} when it does not exist
+     * @api.status 200 org.apache.unomi.api.Persona Persona found, or empty body when missing.
+     * @api.example {"itemId":"persona-vip","itemType":"persona","properties":{"firstName":"VIP"},"segments":["vip"]}
      */
     @GET
     @Path("/personas/{personaId}")
@@ -321,6 +363,8 @@ public class ProfileServiceEndPoint {
      *
      * @param personaId the persona identifier
      * @return the persona and its sessions
+     * @api.status 200 org.apache.unomi.api.PersonaWithSessions Persona bundled with its sessions.
+     * @api.example {"persona":{"itemId":"persona-vip","itemType":"persona","properties":{"firstName":"VIP"}},"sessions":[{"itemId":"ps-1","profileId":"persona-vip","scope":"mysite"}]}
      */
     @GET
     @Path("/personasWithSessions/{personaId}")
@@ -333,6 +377,9 @@ public class ProfileServiceEndPoint {
      *
      * @param personaWithSessions the persona and sessions to persist
      * @return the saved persona and sessions
+     * @api.status 200 org.apache.unomi.api.PersonaWithSessions Persona and sessions saved.
+     * @api.status 400 empty Invalid or unreadable body.
+     * @api.example {"persona":{"itemId":"persona-vip","itemType":"persona","properties":{"firstName":"VIP"}},"sessions":[{"itemId":"ps-1","profileId":"persona-vip","scope":"mysite"}]}
      */
     @POST
     @Path("/personasWithSessions")
@@ -345,6 +392,9 @@ public class ProfileServiceEndPoint {
      *
      * @param persona the persona to persist
      * @return the newly persisted persona
+     * @api.status 200 org.apache.unomi.api.Persona Persona saved.
+     * @api.status 400 empty Invalid or unreadable persona body.
+     * @api.example {"itemId":"persona-vip","itemType":"persona","properties":{"firstName":"VIP"},"segments":["vip"]}
      */
     @POST
     @Path("/personas")
@@ -357,6 +407,8 @@ public class ProfileServiceEndPoint {
      *
      * @param personaId the identifier of the persona to delete
      * @param persona   {@code true} if the specified identifier is supposed to refer to a persona, {@code false} if it is supposed to refer to a profile
+     * @api.status 204 empty Persona deleted.
+     * @api.example {"itemId":"persona-vip","itemType":"persona","properties":{"firstName":"VIP"}}
      */
     @DELETE
     @Path("/personas/{personaId}")
@@ -369,6 +421,8 @@ public class ProfileServiceEndPoint {
      *
      * @param personaId the identifier to use for the new persona
      * @return the newly created persona
+     * @api.status 200 org.apache.unomi.api.Persona Persona created with an initial session.
+     * @api.example {"itemId":"persona-vip","itemType":"persona"}
      */
     @PUT
     @Path("/personas/{personaId}")
@@ -385,6 +439,8 @@ public class ProfileServiceEndPoint {
      * @param size maximum number of results to return, or {@code -1} for all matches
      * @param sortBy optional comma-separated sort fields with optional {@code :asc} or {@code :desc}
      * @return a paged list of persona sessions
+     * @api.status 200 org.apache.unomi.api.PartialList Persona sessions page (list items are PersonaSession).
+     * @api.example {"list":[{"itemId":"ps-1","profileId":"persona-vip","scope":"mysite"}],"offset":0,"pageSize":1,"totalSize":1,"totalSizeRelation":"EQUAL"}
      */
     @GET
     @Path("/personas/{personaId}/sessions")
@@ -397,10 +453,13 @@ public class ProfileServiceEndPoint {
 
     /**
      * Returns the session with the given ID.
+     * When the session does not exist the endpoint may return {@code null} (HTTP 200 with empty body).
      *
      * @param sessionId the session identifier
      * @return the session
      * @throws ParseException if a stored date hint cannot be parsed
+     * @api.status 200 org.apache.unomi.api.Session Session found, or empty body when missing.
+     * @api.example {"itemId":"session-1","itemType":"session","profileId":"profile-1","scope":"mysite","size":4,"duration":1800000}
      */
     @GET
     @Path("/sessions/{sessionId}")
@@ -413,6 +472,9 @@ public class ProfileServiceEndPoint {
      *
      * @param session the session to be saved
      * @return the newly saved session
+     * @api.status 200 org.apache.unomi.api.Session Session saved.
+     * @api.status 400 empty Invalid or unreadable session body.
+     * @api.example {"itemId":"session-1","itemType":"session","profileId":"profile-1","scope":"mysite"}
      */
     @POST
     @Path("/sessions/{sessionId}")
@@ -424,6 +486,8 @@ public class ProfileServiceEndPoint {
      * Deletes the session with the given ID.
      *
      * @param sessionId the session identifier
+     * @api.status 204 empty Session deleted.
+     * @api.example {"itemId":"session-1","itemType":"session","profileId":"profile-42","scope":"mysite"}
      */
     @DELETE
     @Path("/sessions/{sessionId}")
@@ -443,6 +507,8 @@ public class ProfileServiceEndPoint {
      * @param size maximum number of results to return, or {@code -1} for all matches
      * @param sortBy optional comma-separated sort fields with optional {@code :asc} or {@code :desc}
      * @return a paged list of matching events
+     * @api.status 200 org.apache.unomi.api.PartialList Events page (list items are Event).
+     * @api.example {"list":[{"itemId":"evt-1","itemType":"event","eventType":"view","sessionId":"session-1","profileId":"profile-1","scope":"mysite"}],"offset":0,"pageSize":1,"totalSize":1,"totalSizeRelation":"EQUAL"}
      */
     @GET
     @Path("/sessions/{sessionId}/events")
@@ -489,6 +555,9 @@ public class ProfileServiceEndPoint {
      * @param response the HTTP response used to signal missing query parameters
      * @return matching property types, or {@code null} when required parameters are missing
      * @throws IOException if sending the error response fails
+     * @api.status 200 array org.apache.unomi.api.PropertyType Property types already used for the item type/tag.
+     * @api.status 400 empty Missing mandatory {@code tag} or {@code itemType} query parameter.
+     * @api.example [{"itemId":"email","itemType":"propertyType","metadata":{"id":"email","name":"Email","scope":"systemscope"},"target":"profiles","valueTypeId":"email"}]
      */
     @GET
     @Path("/existingProperties")
@@ -513,6 +582,8 @@ public class ProfileServiceEndPoint {
      *
      * @param language the requested locale for property descriptions (currently unused)
      * @return target name to property type mappings
+     * @api.status 200 empty Map of target name to property type collections.
+     * @api.example {"profiles":[{"itemId":"email","itemType":"propertyType","metadata":{"id":"email","name":"Email","scope":"systemscope"},"target":"profiles","valueTypeId":"email"}]}
      */
     @GET
     @Path("/properties")
@@ -528,6 +599,8 @@ public class ProfileServiceEndPoint {
      * @param propertyId the property identifier
      * @param language the requested locale for property descriptions (currently unused)
      * @return the property type
+     * @api.status 200 org.apache.unomi.api.PropertyType Property type found, or empty body when missing.
+     * @api.example {"itemId":"email","itemType":"propertyType","metadata":{"id":"email","name":"Email","scope":"systemscope"},"target":"profiles","valueTypeId":"email"}
      */
     @GET
     @Path("/properties/{propertyId}")
@@ -543,6 +616,8 @@ public class ProfileServiceEndPoint {
      * @param target the property target name
      * @param language the requested locale for property descriptions (currently unused)
      * @return property types for the target
+     * @api.status 200 array org.apache.unomi.api.PropertyType Property types for the target.
+     * @api.example [{"itemId":"email","itemType":"propertyType","metadata":{"id":"email","name":"Email","scope":"systemscope"},"target":"profiles","valueTypeId":"email"},{"itemId":"firstName","itemType":"propertyType","metadata":{"id":"firstName","name":"First Name","scope":"systemscope"},"target":"profiles","valueTypeId":"text"}]
      */
     @GET
     @Path("/properties/targets/{target}")
@@ -559,6 +634,8 @@ public class ProfileServiceEndPoint {
      * @param tags comma-separated tag identifiers
      * @param language the requested locale for property descriptions (currently unused)
      * @return matching property types
+     * @api.status 200 array org.apache.unomi.api.PropertyType Property types matching any of the tags.
+     * @api.example [{"itemId":"email","itemType":"propertyType","metadata":{"id":"email","name":"Email","scope":"systemscope","tags":["contact"]},"target":"profiles","valueTypeId":"email"}]
      */
     @GET
     @Path("/properties/tags/{tags}")
@@ -580,6 +657,8 @@ public class ProfileServiceEndPoint {
      * @param tags comma-separated system tag identifiers
      * @param language the requested locale for property descriptions (currently unused)
      * @return matching property types
+     * @api.status 200 array org.apache.unomi.api.PropertyType Property types matching any of the system tags.
+     * @api.example [{"itemId":"email","itemType":"propertyType","metadata":{"id":"email","name":"Email","scope":"systemscope","systemTags":["contactInfo"]},"target":"profiles","valueTypeId":"email"}]
      */
     @GET
     @Path("/properties/systemTags/{tags}")
@@ -598,7 +677,10 @@ public class ProfileServiceEndPoint {
      * Property-type lookup helpers that may move to a dedicated endpoint in a future release.
      *
      * @param property the property type to persist
-     * @return {@code true} if the property type was properly created, {@code false} otherwise (for example, if the property type already existed
+     * @return {@code true} if the property type was properly created, {@code false} otherwise (for example if it already existed)
+     * @api.status 200 empty {@code true} when created/updated, {@code false} when not persisted.
+     * @api.status 400 empty Invalid or unreadable property type body.
+     * @api.example true
      */
     @POST
     @Path("/properties")
@@ -612,7 +694,10 @@ public class ProfileServiceEndPoint {
      * Property-type lookup helpers that may move to a dedicated endpoint in a future release.
      *
      * @param properties the properties type to persist
-     * @return {@code true} if the property type was properly created, {@code false} otherwise (for example, if the property type already existed
+     * @return {@code true} if at least one property type was persisted, {@code false} otherwise
+     * @api.status 200 empty {@code true} when at least one property type was saved.
+     * @api.status 400 empty Invalid or unreadable property type list body.
+     * @api.example true
      */
     @POST
     @Path("/properties/bulk")
@@ -631,6 +716,8 @@ public class ProfileServiceEndPoint {
      *
      * @param propertyId the identifier of the property type to delete
      * @return {@code true} if the property type was properly deleted, {@code false} otherwise
+     * @api.status 200 empty {@code true} when deleted, {@code false} when not found / not deleted.
+     * @api.example true
      */
     @DELETE
     @Path("/properties/{propertyId}")
@@ -643,6 +730,9 @@ public class ProfileServiceEndPoint {
      *
      * @param query the search query
      * @return a paged list of matching sessions
+     * @api.status 200 org.apache.unomi.api.PartialList Sessions page (list items are Session).
+     * @api.status 400 empty Invalid or unreadable query body.
+     * @api.example {"list":[{"itemId":"session-1","itemType":"session","profileId":"profile-1","scope":"mysite"}],"offset":0,"pageSize":1,"totalSize":1,"totalSizeRelation":"EQUAL"}
      */
     @POST
     @Path("/search/sessions")
@@ -652,10 +742,13 @@ public class ProfileServiceEndPoint {
 
     /**
      * Adds an alias to a profile.
+     * Links a client-specific alias id to the canonical {@code profileId}. When {@code X-Unomi-ClientId} is omitted, {@code defaultClientId} is used.
      *
      * @param profileId the profile identifier
      * @param aliasId the alias identifier
      * @param headerClientID optional client identifier from the request header
+     * @api.status 204 empty Alias linked to the profile.
+     * @api.example {"itemId":"alias-crm-9","itemType":"profileAlias","profileID":"profile-42","clientID":"web-tracker"}
      */
     @POST
     @Path("/{profileId}/aliases/{aliasId}")
@@ -672,6 +765,8 @@ public class ProfileServiceEndPoint {
      * @param profileId the profile identifier
      * @param aliasId the alias identifier
      * @param headerClientID optional client identifier from the request header
+     * @api.status 204 empty Alias removed from the profile.
+     * @api.example {"itemId":"alias-crm-9","itemType":"profileAlias","profileID":"profile-42","clientID":"web-tracker"}
      */
     @DELETE
     @Path("/{profileId}/aliases/{aliasId}")
@@ -690,6 +785,8 @@ public class ProfileServiceEndPoint {
      * @param size page size
      * @param sortBy optional sort field
      * @return the matching profile aliases
+     * @api.status 200 org.apache.unomi.api.PartialList Aliases page (list items are ProfileAlias).
+     * @api.example {"list":[{"itemId":"alias-crm-9","itemType":"profileAlias","profileID":"profile-1","clientID":"web-tracker"}],"offset":0,"pageSize":1,"totalSize":1,"totalSizeRelation":"EQUAL"}
      */
     @GET
     @Path("/{profileId}/aliases")
diff --git a/rest/src/main/java/org/apache/unomi/rest/endpoints/QueryServiceEndPoint.java b/rest/src/main/java/org/apache/unomi/rest/endpoints/QueryServiceEndPoint.java
index d52844361..d97b3ce79 100644
--- a/rest/src/main/java/org/apache/unomi/rest/endpoints/QueryServiceEndPoint.java
+++ b/rest/src/main/java/org/apache/unomi/rest/endpoints/QueryServiceEndPoint.java
@@ -80,6 +80,8 @@ public class QueryServiceEndPoint {
      * @param property the property whose distinct values form aggregation buckets
      * @return property value to item count mappings
      * @see Item Item for a discussion of {@code ITEM_TYPE}
+     * @api.status 200 empty Property value to item count map (may be empty).
+     * @api.example {"vip":42,"standard":128}
      */
     @GET
     @Path("/{type}/{property}")
@@ -98,6 +100,8 @@ public class QueryServiceEndPoint {
      * @param aggregateQuery optional aggregate query constraints
      * @return property value to item count mappings
      * @see Item Item for a discussion of {@code ITEM_TYPE}
+     * @api.status 200 empty Property value to item count map (may be empty).
+     * @api.example {"FR":1200,"DE":980}
      */
     @POST
     @Path("/{type}/{property}")
@@ -112,13 +116,18 @@ public class QueryServiceEndPoint {
 
     /**
      * Returns numeric metrics for a field on items that match the given condition.
+     * <p>
+     * Example path: {@code POST /cxs/query/profile/properties.nbOfVisits/sum/avg} with a condition body
+     * returns visit-count metrics across matching profiles.
      *
      * @param condition the condition matching items must satisfy
      * @param metricsType slash-separated metric names ({@code sum}, {@code avg}, {@code min}, {@code max})
-     * @param property the numeric field to aggregate
-     * @param type the item type name from the class {@code ITEM_TYPE} field
+     * @param property the numeric field to aggregate (e.g. {@code properties.nbOfVisits})
+     * @param type the item type name from the class {@code ITEM_TYPE} field (e.g. {@code profile})
      * @return metric name to computed value mappings
      * @see Item Item for a discussion of {@code ITEM_TYPE}
+     * @api.status 200 empty Metric name to computed value map (keys are the requested metric names).
+     * @api.example {"sum":12500.0,"avg":250.0,"min":1.0,"max":900.0}
      */
     @POST
     @Path("/{type}/{property}/{metricTypes:((sum|avg|min|max)/?)*}")
@@ -135,6 +144,9 @@ public class QueryServiceEndPoint {
      * @param response the HTTP response used to set status on validation failure
      * @return the matching item count, or {@code 0} when validation fails
      * @see Item Item for a discussion of {@code ITEM_TYPE}
+     * @api.status 200 empty Match count as a JSON number.
+     * @api.status 400 empty Invalid or unreadable condition when {@code validate} is {@code true} or omitted.
+     * @api.example 42
      */
     @POST
     @Path("/{type}/count")
diff --git a/rest/src/main/java/org/apache/unomi/rest/endpoints/RulesServiceEndPoint.java b/rest/src/main/java/org/apache/unomi/rest/endpoints/RulesServiceEndPoint.java
index a908d5373..018afb51f 100644
--- a/rest/src/main/java/org/apache/unomi/rest/endpoints/RulesServiceEndPoint.java
+++ b/rest/src/main/java/org/apache/unomi/rest/endpoints/RulesServiceEndPoint.java
@@ -74,6 +74,8 @@ public class RulesServiceEndPoint {
      * Note that this includes only rules currently loaded in memory, not every rule in storage.
      *
      * @return known rule metadata
+     * @api.status 200 array org.apache.unomi.api.Metadata Rule metadata currently loaded in memory (may be empty).
+     * @api.example [{"id":"set-premium-on-view","name":"Set Premium on View","scope":"mysite","enabled":true}]
      */
     @GET
     @Path("/")
@@ -83,8 +85,13 @@ public class RulesServiceEndPoint {
 
     /**
      * Persists the specified rule to the context server.
+     * Body is a full {@link Rule}: {@code metadata}, a {@code condition} (JSON field {@code type} + {@code parameterValues}),
+     * and an {@code actions} list (each with {@code type} + {@code parameterValues}).
      *
      * @param rule the rule to be persisted
+     * @api.status 204 empty Rule created or updated.
+     * @api.status 400 empty Invalid or unreadable rule body (condition/actions schema).
+     * @api.example {"itemId":"set-premium-on-view","itemType":"rule","metadata":{"id":"set-premium-on-view","name":"Set Premium on View","scope":"mysite","enabled":true,"description":"Mark profile premium when a view event arrives"},"condition":{"type":"eventTypeCondition","parameterValues":{"eventTypeId":"view"}},"actions":[{"type":"setPropertyAction","parameterValues":{"setPropertyName":"properties.isPremium","setPropertyValueBoolean":true,"storeInSession":false}}],"priority":0,"raise [...]
      */
     @POST
     @Path("/")
@@ -96,6 +103,8 @@ public class RulesServiceEndPoint {
      * Returns execution statistics for all known rules.
      *
      * @return rule ID to statistics mappings
+     * @api.status 200 empty Map of rule id to {@link RuleStatistics}.
+     * @api.example {"set-premium-on-view":{"itemId":"set-premium-on-view","itemType":"rulestats","executionCount":42,"localExecutionCount":3,"conditionsTime":120,"actionsTime":95}}
      */
     @GET
     @Path("/statistics")
@@ -105,6 +114,9 @@ public class RulesServiceEndPoint {
 
     /**
      * Resets execution statistics for all rules to zero.
+     *
+     * @api.status 204 empty All rule statistics reset.
+     * @api.example {}
      */
     @DELETE
     @Path("/statistics")
@@ -117,6 +129,9 @@ public class RulesServiceEndPoint {
      *
      * @param query the query rules must match
      * @return a paged list of matching rule metadata
+     * @api.status 200 org.apache.unomi.api.PartialList Metadata page (list items are Metadata).
+     * @api.status 400 empty Invalid or unreadable query body.
+     * @api.example {"list":[{"id":"set-premium-on-view","name":"Set Premium on View","scope":"mysite","enabled":true}],"offset":0,"pageSize":1,"totalSize":1,"totalSizeRelation":"EQUAL"}
      */
     @POST
     @Path("/query")
@@ -126,9 +141,13 @@ public class RulesServiceEndPoint {
 
     /**
      * Returns full rule definitions matching the given query.
+     * Each list element includes {@code condition} and {@code actions} (see {@link Rule}).
      *
      * @param query the query specifying which rules to include
      * @return a paged list of matching rules
+     * @api.status 200 org.apache.unomi.api.PartialList Rules page (list items are Rule).
+     * @api.status 400 empty Invalid or unreadable query body.
+     * @api.example {"list":[{"itemId":"set-premium-on-view","itemType":"rule","metadata":{"id":"set-premium-on-view","name":"Set Premium on View","scope":"mysite","enabled":true,"description":"Mark profile premium when a view event arrives"},"condition":{"type":"eventTypeCondition","parameterValues":{"eventTypeId":"view"}},"actions":[{"type":"setPropertyAction","parameterValues":{"setPropertyName":"properties.isPremium","setPropertyValueBoolean":true,"storeInSession":false}}],"priority" [...]
      */
     @POST
     @Path("/query/detailed")
@@ -138,9 +157,14 @@ public class RulesServiceEndPoint {
 
     /**
      * Returns the rule with the given ID.
+     * When the rule does not exist the endpoint returns {@code null} (HTTP 200 with empty body).
+     * Use the embedded {@code condition}/{@code actions} objects as templates for new rules
+     * ({@code type} + {@code parameterValues} on each).
      *
      * @param ruleId the rule identifier
      * @return the rule, or {@code null} when it does not exist
+     * @api.status 200 org.apache.unomi.api.rules.Rule Rule found, or empty body when missing.
+     * @api.example {"itemId":"set-premium-on-view","itemType":"rule","metadata":{"id":"set-premium-on-view","name":"Set Premium on View","scope":"mysite","enabled":true,"description":"Mark profile premium when a view event arrives"},"condition":{"type":"eventTypeCondition","parameterValues":{"eventTypeId":"view"}},"actions":[{"type":"setPropertyAction","parameterValues":{"setPropertyName":"properties.isPremium","setPropertyValueBoolean":true,"storeInSession":false}}],"priority":0,"raise [...]
      */
     @GET
     @Path("/{ruleId}")
@@ -153,6 +177,8 @@ public class RulesServiceEndPoint {
      *
      * @param ruleId the rule identifier
      * @return the rule statistics, or {@code null} when the rule does not exist
+     * @api.status 200 org.apache.unomi.api.rules.RuleStatistics Statistics for the rule, or empty body when missing.
+     * @api.example {"itemId":"set-premium-on-view","itemType":"rulestats","executionCount":42,"localExecutionCount":3,"conditionsTime":120,"actionsTime":95}
      */
     @GET
     @Path("/{ruleId}/statistics")
@@ -164,6 +190,8 @@ public class RulesServiceEndPoint {
      * Deletes the rule identified by the specified identifier.
      *
      * @param ruleId the identifier of the rule we want to delete
+     * @api.status 204 empty Rule deleted.
+     * @api.example {"itemId":"set-premium-on-view","itemType":"rule","metadata":{"id":"set-premium-on-view","name":"Set Premium on View","scope":"mysite","enabled":true,"description":"Mark profile premium when a view event arrives"},"condition":{"type":"eventTypeCondition","parameterValues":{"eventTypeId":"view"}},"actions":[{"type":"setPropertyAction","parameterValues":{"setPropertyName":"properties.isPremium","setPropertyValueBoolean":true,"storeInSession":false}}],"priority":0,"raise [...]
      */
     @DELETE
     @Path("/{ruleId}")
@@ -173,8 +201,11 @@ public class RulesServiceEndPoint {
 
     /**
      * Deprecated maintenance endpoint kept for backward compatibility.
+     * Reloads every in-memory rule from storage by re-saving it.
      *
      * @deprecated As of version 1.1.0-incubating, not needed anymore
+     * @api.status 204 empty In-memory rules re-persisted.
+     * @api.example {}
      */
     @Deprecated
     @GET
diff --git a/rest/src/main/java/org/apache/unomi/rest/endpoints/ScopeServiceEndPoint.java b/rest/src/main/java/org/apache/unomi/rest/endpoints/ScopeServiceEndPoint.java
index cbee62b60..6b0a5b4df 100644
--- a/rest/src/main/java/org/apache/unomi/rest/endpoints/ScopeServiceEndPoint.java
+++ b/rest/src/main/java/org/apache/unomi/rest/endpoints/ScopeServiceEndPoint.java
@@ -65,6 +65,8 @@ public class ScopeServiceEndPoint {
      * Returns all configured scopes.
      *
      * @return all known scopes
+     * @api.status 200 array org.apache.unomi.api.Scope All scopes (may be empty).
+     * @api.example [{"itemId":"systemscope","itemType":"scope","metadata":{"id":"systemscope","name":"System scope","scope":"systemscope","enabled":true}}]
      */
     @GET
     @Path("/")
@@ -76,6 +78,8 @@ public class ScopeServiceEndPoint {
      *
      * @param scope the scope to be persisted
      * @return an empty success response
+     * @api.status 200 empty Scope created or updated.
+     * @api.example {"itemId":"mysite","itemType":"scope","metadata":{"id":"mysite","name":"My site","scope":"mysite","enabled":true}}
      */
     @POST
     @Path("/")
@@ -86,9 +90,12 @@ public class ScopeServiceEndPoint {
 
     /**
      * Returns the scope with the given ID.
+     * When the scope does not exist the endpoint returns {@code null} (HTTP 200 with empty body).
      *
      * @param scopeId the scope identifier
      * @return the scope, or {@code null} when it does not exist
+     * @api.status 200 org.apache.unomi.api.Scope Scope found, or empty body when missing.
+     * @api.example {"itemId":"mysite","itemType":"scope","metadata":{"id":"mysite","name":"My site","scope":"mysite","enabled":true}}
      */
     @GET
     @Path("/{scopeId}")
@@ -97,9 +104,11 @@ public class ScopeServiceEndPoint {
     }
 
     /**
-     * Deletes a scope.
+     * Deletes the scope with the given ID.
      *
      * @param scopeId the identifier of the scope
+     * @api.status 204 empty Scope deleted.
+     * @api.example {"itemId":"mysite","itemType":"scope","metadata":{"id":"mysite","name":"My site","scope":"mysite","enabled":true}}
      */
     @DELETE
     @Path("/{scopeId}")
diff --git a/rest/src/main/java/org/apache/unomi/rest/endpoints/ScoringServiceEndPoint.java b/rest/src/main/java/org/apache/unomi/rest/endpoints/ScoringServiceEndPoint.java
index 8d78bf84b..a5315c798 100644
--- a/rest/src/main/java/org/apache/unomi/rest/endpoints/ScoringServiceEndPoint.java
+++ b/rest/src/main/java/org/apache/unomi/rest/endpoints/ScoringServiceEndPoint.java
@@ -75,6 +75,8 @@ public class ScoringServiceEndPoint {
      * @param size maximum number of results to return, or {@code -1} for all matches
      * @param sortBy optional comma-separated sort fields with optional {@code :asc} or {@code :desc}
      * @return matching scoring metadata
+     * @api.status 200 array org.apache.unomi.api.Metadata Scoring metadata for the requested page (may be empty).
+     * @api.example [{"id":"engagement-score","name":"Engagement score","scope":"mysite","enabled":true}]
      */
     @GET
     @Path("/")
@@ -87,6 +89,9 @@ public class ScoringServiceEndPoint {
      *
      * @param query the query scorings must match
      * @return a paged list of matching scoring metadata
+     * @api.status 200 org.apache.unomi.api.PartialList Metadata page (list items are Metadata).
+     * @api.status 400 empty Invalid or unreadable query body.
+     * @api.example {"list":[{"id":"engagement-score","name":"Engagement score","scope":"mysite","enabled":true}],"offset":0,"pageSize":1,"totalSize":1,"totalSizeRelation":"EQUAL"}
      */
     @POST
     @Path("/query")
@@ -96,9 +101,12 @@ public class ScoringServiceEndPoint {
 
     /**
      * Returns the scoring definition with the given ID.
+     * When the scoring does not exist the endpoint returns {@code null} (HTTP 200 with empty body).
      *
      * @param scoringId the scoring identifier
      * @return the scoring, or {@code null} when it does not exist
+     * @api.status 200 org.apache.unomi.api.segments.Scoring Scoring found, or empty body when missing.
+     * @api.example {"itemId":"engagement-score","itemType":"scoring","metadata":{"id":"engagement-score","name":"Engagement score","scope":"mysite","enabled":true},"elements":[{"condition":{"type":"eventTypeCondition","parameterValues":{"eventTypeId":"view"}},"value":10}]}
      */
     @GET
     @Path("/{scoringID}")
@@ -108,8 +116,13 @@ public class ScoringServiceEndPoint {
 
     /**
      * Persists the specified scoring in the context server.
+     * Body is a full {@link Scoring}: {@code metadata} plus {@code elements}, each with a
+     * {@code condition} (JSON field {@code type} + {@code parameterValues}) and a point {@code value}.
      *
      * @param scoring the scoring to be persisted
+     * @api.status 204 empty Scoring created or updated.
+     * @api.status 400 empty Invalid or unreadable scoring body (condition schema / validation).
+     * @api.example {"itemId":"engagement-score","itemType":"scoring","metadata":{"id":"engagement-score","name":"Engagement score","scope":"mysite","enabled":true},"elements":[{"condition":{"type":"eventTypeCondition","parameterValues":{"eventTypeId":"view"}},"value":10}]}
      */
     @POST
     @Path("/")
@@ -125,6 +138,8 @@ public class ScoringServiceEndPoint {
      * @param scoringName        the name of the new scoring
      * @param scoringDescription the description of the new scoring
      * @see Item Item's description for a discussion of scope
+     * @api.status 204 empty Scoring created.
+     * @api.example {"itemId":"engagement-score","itemType":"scoring","metadata":{"id":"engagement-score","name":"Engagement score","scope":"mysite","enabled":true},"elements":[{"condition":{"type":"eventTypeCondition","parameterValues":{"eventTypeId":"view"}},"value":10}]}
      */
     @PUT
     @Path("/{scope}/{scoringID}")
@@ -134,14 +149,17 @@ public class ScoringServiceEndPoint {
     }
 
     /**
-     * Removes the scoring definition identified by the specified identifier. We can specify that we want the operation to be validated beforehand so that we can
-     * know if any other segment that might use the segment we're trying to delete as a condition might be impacted. If {@code validate} is set to {@code false}, no
-     * validation is performed. If set to {@code true}, we will first check if any segment or scoring depends on the scoring we're trying to delete and if so we will not delete the
-     * scoring but rather return the list of the metadata of the impacted items. If no dependents are found, then we properly delete the scoring.
+     * Removes the scoring definition identified by the specified identifier.
+     * <p>
+     * When {@code validate} is {@code true}, dependents are checked first: if any segment or scoring
+     * references this scoring, it is <strong>not</strong> deleted and the impacted metadata is returned.
+     * When {@code validate} is {@code false}, the scoring is deleted without that check.
      *
      * @param scoringId the identifier of the scoring we want to delete
-     * @param validate  whether or not to perform validation
-     * @return a list of impacted items metadata if any or an empty list if none were found or validation was skipped
+     * @param validate  whether or not to perform dependency validation before delete
+     * @return dependent metadata when delete was blocked; empty lists when deleted or nothing depended on it
+     * @api.status 200 org.apache.unomi.api.segments.DependentMetadata Empty lists when deleted; otherwise dependents that blocked delete.
+     * @api.example {"segments":[],"scorings":[{"id":"loyalty-score","name":"Loyalty score","scope":"mysite","enabled":true}]}
      */
     @DELETE
     @Path("/{scoringID}")
@@ -152,10 +170,13 @@ public class ScoringServiceEndPoint {
     /**
      * Returns segment and scoring metadata that depend on the given scoring.
      * <p>
-     * A dependent definition includes a scoring condition that references this scoring.
+     * A dependent definition includes a scoring condition that references this scoring
+     * (useful before delete or when editing related definitions).
      *
      * @param scoringId the scoring identifier
      * @return metadata for dependent segments and scorings
+     * @api.status 200 org.apache.unomi.api.segments.DependentMetadata Dependent segments/scorings (lists may be empty).
+     * @api.example {"segments":[],"scorings":[]}
      */
     @GET
     @Path("/{scoringID}/impacted")
@@ -165,8 +186,11 @@ public class ScoringServiceEndPoint {
 
     /**
      * Deprecated maintenance endpoint kept for backward compatibility.
+     * Reloads in-memory scorings from storage by re-saving each definition.
      *
      * @deprecated As of version 1.1.0-incubating, not needed anymore
+     * @api.status 204 empty In-memory scorings re-persisted.
+     * @api.example {}
      */
     @Deprecated
     @GET
diff --git a/rest/src/main/java/org/apache/unomi/rest/endpoints/SegmentServiceEndPoint.java b/rest/src/main/java/org/apache/unomi/rest/endpoints/SegmentServiceEndPoint.java
index ab4529011..f6b19ac3c 100644
--- a/rest/src/main/java/org/apache/unomi/rest/endpoints/SegmentServiceEndPoint.java
+++ b/rest/src/main/java/org/apache/unomi/rest/endpoints/SegmentServiceEndPoint.java
@@ -69,13 +69,15 @@ public class SegmentServiceEndPoint {
     }
 
     /**
-     * Returns profiles that match the segment with the given ID.
+     * Returns profiles that currently match the segment with the given ID.
      *
      * @param segmentId the segment identifier
      * @param offset zero-based index of the first result
      * @param size maximum number of results to return, or {@code -1} for all matches
      * @param sortBy optional comma-separated sort fields with optional {@code :asc} or {@code :desc}
      * @return a paged list of matching profiles
+     * @api.status 200 org.apache.unomi.api.PartialList Profiles page (list items are Profile).
+     * @api.example {"list":[{"itemId":"profile-1","itemType":"profile","properties":{"isPremium":true}}],"offset":0,"pageSize":1,"totalSize":1,"totalSizeRelation":"EQUAL"}
      */
     @GET
     @Path("/{segmentID}/match")
@@ -84,10 +86,12 @@ public class SegmentServiceEndPoint {
     }
 
     /**
-     * Returns how many profiles match the segment with the given ID.
+     * Returns how many profiles currently match the segment with the given ID.
      *
      * @param segmentId the segment identifier
      * @return the number of matching profiles
+     * @api.status 200 empty Match count as a JSON number.
+     * @api.example 42
      */
     @GET
     @Path("/{segmentID}/count")
@@ -96,11 +100,15 @@ public class SegmentServiceEndPoint {
     }
 
     /**
-     * Determines whether the specified profile is part of the segment identified by the specified identifier.
+     * Determines whether the specified profile is a member of the given segment.
+     * <p>
+     * The {@code profile} path variable is the profile identifier (the JAX-RS type is {@link Profile} for binding).
      *
-     * @param profile   the profile we want to check
+     * @param profile   the profile to check (bound from the profile id path segment)
      * @param segmentId the identifier of the segment against which we want to check the profile
      * @return {@code true} if the specified profile is in the specified segment, {@code false} otherwise
+     * @api.status 200 empty Membership flag.
+     * @api.example true
      */
     @GET
     @Path("/{segmentID}/match/{profile}")
@@ -115,6 +123,8 @@ public class SegmentServiceEndPoint {
      * @param size maximum number of results to return, or {@code -1} for all matches
      * @param sortBy optional comma-separated sort fields with optional {@code :asc} or {@code :desc}
      * @return matching segment metadata
+     * @api.status 200 array org.apache.unomi.api.Metadata Segment metadata for the requested page (may be empty).
+     * @api.example [{"id":"premium-profiles","name":"Premium profiles","scope":"mysite","enabled":true}]
      */
     @GET
     @Path("/")
@@ -125,10 +135,13 @@ public class SegmentServiceEndPoint {
     /**
      * Returns segment and scoring metadata that depend on the given segment.
      * <p>
-     * A dependent definition includes a profile-segment condition that references this segment.
+     * A dependent definition includes a profile-segment condition that references this segment
+     * (useful before delete or when editing related definitions).
      *
      * @param segmentId the segment identifier
      * @return metadata for dependent segments and scorings
+     * @api.status 200 org.apache.unomi.api.segments.DependentMetadata Dependent segments/scorings (lists may be empty).
+     * @api.example {"segments":[],"scorings":[]}
      */
     @GET
     @Path("/{segmentID}/impacted")
@@ -138,8 +151,13 @@ public class SegmentServiceEndPoint {
 
     /**
      * Persists the specified segment in the context server.
+     * Body is a full {@link Segment}: {@code metadata} plus a membership {@code condition}
+     * (JSON field {@code type} + {@code parameterValues}).
      *
      * @param segment the segment to be persisted
+     * @api.status 204 empty Segment created or updated.
+     * @api.status 400 empty Invalid or unreadable segment body (condition schema / validation).
+     * @api.example {"itemId":"premium-profiles","itemType":"segment","metadata":{"id":"premium-profiles","name":"Premium profiles","scope":"mysite","enabled":true,"description":"Profiles marked as premium"},"condition":{"type":"profilePropertyCondition","parameterValues":{"propertyName":"properties.isPremium","comparisonOperator":"equals","propertyValue":"true"}}}
      */
     @POST
     @Path("/")
@@ -152,6 +170,9 @@ public class SegmentServiceEndPoint {
      *
      * @param query the query segments must match
      * @return a paged list of matching segment metadata
+     * @api.status 200 org.apache.unomi.api.PartialList Metadata page (list items are Metadata).
+     * @api.status 400 empty Invalid or unreadable query body.
+     * @api.example {"list":[{"id":"premium-profiles","name":"Premium profiles","scope":"mysite","enabled":true}],"offset":0,"pageSize":1,"totalSize":1,"totalSizeRelation":"EQUAL"}
      */
     @POST
     @Path("/query")
@@ -161,9 +182,12 @@ public class SegmentServiceEndPoint {
 
     /**
      * Returns the segment definition with the given ID.
+     * When the segment does not exist the endpoint returns {@code null} (HTTP 200 with empty body).
      *
      * @param segmentId the segment identifier
      * @return the segment, or {@code null} when it does not exist
+     * @api.status 200 org.apache.unomi.api.segments.Segment Segment found, or empty body when missing.
+     * @api.example {"itemId":"premium-profiles","itemType":"segment","metadata":{"id":"premium-profiles","name":"Premium profiles","scope":"mysite","enabled":true,"description":"Profiles marked as premium"},"condition":{"type":"profilePropertyCondition","parameterValues":{"propertyName":"properties.isPremium","comparisonOperator":"equals","propertyValue":"true"}}}
      */
     @GET
     @Path("/{segmentID}")
@@ -172,14 +196,17 @@ public class SegmentServiceEndPoint {
     }
 
     /**
-     * Removes the segment definition identified by the specified identifier. We can specify that we want the operation to be validated beforehand so that we can
-     * know if any other segment that might use the segment we're trying to delete as a condition might be impacted. If {@code validate} is set to {@code false}, no
-     * validation is performed. If set to {@code true}, we will first check if any segment or scoring depends on the segment we're trying to delete and if so we will not delete the
-     * segment but rather return the list of the metadata of the impacted items. If no dependents are found, then we properly delete the segment.
+     * Removes the segment definition identified by the specified identifier.
+     * <p>
+     * When {@code validate} is {@code true}, dependents are checked first: if any segment or scoring
+     * references this segment, it is <strong>not</strong> deleted and the impacted metadata is returned.
+     * When {@code validate} is {@code false}, the segment is deleted without that check.
      *
      * @param segmentId the identifier of the segment we want to delete
-     * @param validate  whether or not to perform validation
-     * @return a list of impacted segment metadata if any or an empty list if none were found or validation was skipped
+     * @param validate  whether or not to perform dependency validation before delete
+     * @return dependent metadata when delete was blocked; empty lists when deleted or nothing depended on it
+     * @api.status 200 org.apache.unomi.api.segments.DependentMetadata Empty lists when deleted; otherwise dependents that blocked delete.
+     * @api.example {"segments":[{"id":"premium-buyers","name":"Premium buyers","scope":"mysite","enabled":true}],"scorings":[]}
      */
     @DELETE
     @Path("/{segmentID}")
@@ -189,8 +216,11 @@ public class SegmentServiceEndPoint {
 
     /**
      * Deprecated maintenance endpoint kept for backward compatibility.
+     * Reloads in-memory segments from storage by re-saving each definition.
      *
      * @deprecated As of version 1.1.0-incubating, not needed anymore
+     * @api.status 204 empty In-memory segments re-persisted.
+     * @api.example {}
      */
     @Deprecated
     @GET
diff --git a/rest/src/main/java/org/apache/unomi/rest/endpoints/TestEndPoint.java b/rest/src/main/java/org/apache/unomi/rest/endpoints/TestEndPoint.java
index 2d86ec0ec..29f90efca 100644
--- a/rest/src/main/java/org/apache/unomi/rest/endpoints/TestEndPoint.java
+++ b/rest/src/main/java/org/apache/unomi/rest/endpoints/TestEndPoint.java
@@ -47,9 +47,11 @@ public class TestEndPoint {
     }
 
     /**
-     * Returns a simple health-check response.
+     * Returns a simple health-check response for connectivity probes.
      *
-     * @return the ping response
+     * @return the ping response body ({@code pong})
+     * @api.status 200 empty Plain-text {@code pong} when the REST layer is reachable.
+     * @api.example pong
      */
     @GET
     @Path("/ping")
diff --git a/rest/src/main/java/org/apache/unomi/rest/endpoints/UserListServiceEndPoint.java b/rest/src/main/java/org/apache/unomi/rest/endpoints/UserListServiceEndPoint.java
index 5d8e80ea8..c79ac9fd3 100644
--- a/rest/src/main/java/org/apache/unomi/rest/endpoints/UserListServiceEndPoint.java
+++ b/rest/src/main/java/org/apache/unomi/rest/endpoints/UserListServiceEndPoint.java
@@ -73,6 +73,8 @@ public class UserListServiceEndPoint {
      * @param size maximum number of results to return, or {@code -1} for all matches
      * @param sortBy optional comma-separated sort fields with optional {@code :asc} or {@code :desc}
      * @return matching user list metadata
+     * @api.status 200 array org.apache.unomi.api.Metadata User list metadata for the requested page (may be empty).
+     * @api.example [{"id":"newsletter-subscribers","name":"Newsletter subscribers","scope":"mysite","enabled":true}]
      */
     @GET
     @Path("/")
diff --git a/rest/src/main/java/org/apache/unomi/rest/models/EventCollectorResponse.java b/rest/src/main/java/org/apache/unomi/rest/models/EventCollectorResponse.java
index b442c17c1..6cd0c9fe2 100644
--- a/rest/src/main/java/org/apache/unomi/rest/models/EventCollectorResponse.java
+++ b/rest/src/main/java/org/apache/unomi/rest/models/EventCollectorResponse.java
@@ -27,24 +27,15 @@ import java.io.Serializable;
  */
 public class EventCollectorResponse implements Serializable {
     /**
-     * A bitwise combination of EventService flags indicating what was updated during event processing.
-     * The value is composed of the following flags:
-     * <ul>
-     *   <li>0 = NO_CHANGE - No changes occurred</li>
-     *   <li>1 = ERROR - An error occurred during processing</li>
-     *   <li>2 = SESSION_UPDATED - The associated session was updated</li>
-     *   <li>4 = PROFILE_UPDATED - The associated profile was updated</li>
-     * </ul>
-     * Multiple flags can be combined, for example:
-     * <ul>
-     *   <li>6 = SESSION_UPDATED (2) + PROFILE_UPDATED (4) - Both session and profile were updated</li>
-     * </ul>
+     * Bitwise change flags from event processing:
+     * {@code 0}=NO_CHANGE, {@code 1}=ERROR, {@code 2}=SESSION_UPDATED, {@code 4}=PROFILE_UPDATED
+     * (combine with OR, e.g. {@code 6}=session+profile updated).
+     * @api.example 6
      */
     private int updated;
 
     /**
-     * Contains tracing information about the request processing.
-     * This can be used for debugging and monitoring purposes.
+     * Present only when {@code explain=true} and the caller is an administrator / tenant administrator.
      */
     private TraceNode requestTracing;
 
diff --git a/rest/src/main/java/org/apache/unomi/rest/scheduler/TaskEndpoint.java b/rest/src/main/java/org/apache/unomi/rest/scheduler/TaskEndpoint.java
index ee0536692..5fbba51f0 100644
--- a/rest/src/main/java/org/apache/unomi/rest/scheduler/TaskEndpoint.java
+++ b/rest/src/main/java/org/apache/unomi/rest/scheduler/TaskEndpoint.java
@@ -48,14 +48,19 @@ public class TaskEndpoint {
     private SchedulerService schedulerService;
 
     /**
-     * Retrieves all tasks in the system.
+     * Returns scheduled tasks with optional status or type filtering and paging.
+     * <p>
+     * When neither {@code status} nor {@code type} is set, all tasks are loaded and sliced in memory.
      *
-     * @param status optional status filter
-     * @param type optional type filter
-     * @param offset pagination offset
-     * @param limit pagination limit
-     * @param sortBy sort field
-     * @return a partial list of tasks matching the criteria
+     * @param status optional {@link ScheduledTask.TaskStatus} name (case-insensitive)
+     * @param type optional task type filter
+     * @param offset zero-based index of the first result
+     * @param limit maximum number of results to return
+     * @param sortBy optional sort field
+     * @return a paged list of matching tasks
+     * @api.status 200 org.apache.unomi.api.PartialList ScheduledTask page (list items are ScheduledTask; may be empty).
+     * @api.status 400 empty Invalid {@code status} value.
+     * @api.example {"list":[{"itemId":"task-1","itemType":"scheduledTask","taskType":"segmentRefresh","status":"SCHEDULED"}],"offset":0,"pageSize":1,"totalSize":1,"totalSizeRelation":"EQUAL"}
      */
     @GET
     @Produces(MediaType.APPLICATION_JSON)
@@ -87,11 +92,13 @@ public class TaskEndpoint {
     }
 
     /**
-     * Retrieves a specific task by ID.
+     * Returns the scheduled task with the given ID.
      *
-     * @param taskId the ID of the task to retrieve
+     * @param taskId the task identifier
      * @return the requested task
-     * @throws WebApplicationException with 404 status if task is not found
+     * @api.status 200 org.apache.unomi.api.tasks.ScheduledTask Task found.
+     * @api.status 404 empty Task not found.
+     * @api.example {"itemId":"task-1","itemType":"scheduledTask","taskType":"segmentRefresh","status":"SCHEDULED"}
      */
     @GET
     @Path("/{taskId}")
@@ -105,11 +112,13 @@ public class TaskEndpoint {
     }
 
     /**
-     * Cancels a scheduled task.
+     * Cancels the scheduled task with the given ID.
      *
-     * @param taskId the ID of the task to cancel
-     * @return 204 No Content on success
-     * @throws WebApplicationException with 404 status if task is not found
+     * @param taskId the task identifier
+     * @return an empty response on success
+     * @api.status 204 empty Task cancelled.
+     * @api.status 404 empty Task not found.
+     * @api.example {}
      */
     @DELETE
     @Path("/{taskId}")
@@ -123,12 +132,14 @@ public class TaskEndpoint {
     }
 
     /**
-     * Retries a failed task.
+     * Retries a failed scheduled task and returns its updated state.
      *
-     * @param taskId the ID of the task to retry
-     * @param resetFailureCount whether to reset the failure count
+     * @param taskId the task identifier
+     * @param resetFailureCount when {@code true}, resets the failure counter before retry
      * @return the retried task
-     * @throws WebApplicationException with 404 status if task is not found
+     * @api.status 200 org.apache.unomi.api.tasks.ScheduledTask Task retried.
+     * @api.status 404 empty Task not found.
+     * @api.example {"itemId":"task-1","itemType":"scheduledTask","taskType":"segmentRefresh","status":"SCHEDULED"}
      */
     @POST
     @Path("/{taskId}/retry")
@@ -145,11 +156,13 @@ public class TaskEndpoint {
     }
 
     /**
-     * Resumes a crashed task.
+     * Resumes a crashed scheduled task and returns its updated state.
      *
-     * @param taskId the ID of the task to resume
+     * @param taskId the task identifier
      * @return the resumed task
-     * @throws WebApplicationException with 404 status if task is not found
+     * @api.status 200 org.apache.unomi.api.tasks.ScheduledTask Task resumed.
+     * @api.status 404 empty Task not found.
+     * @api.example {"itemId":"task-1","itemType":"scheduledTask","taskType":"segmentRefresh","status":"RUNNING"}
      */
     @POST
     @Path("/{taskId}/resume")
diff --git a/rest/src/main/java/org/apache/unomi/rest/server/RestServer.java b/rest/src/main/java/org/apache/unomi/rest/server/RestServer.java
index 788be2537..81cf960bb 100644
--- a/rest/src/main/java/org/apache/unomi/rest/server/RestServer.java
+++ b/rest/src/main/java/org/apache/unomi/rest/server/RestServer.java
@@ -23,12 +23,14 @@ import org.apache.cxf.Bus;
 import org.apache.cxf.endpoint.Server;
 import org.apache.cxf.interceptor.Interceptor;
 import org.apache.cxf.jaxrs.JAXRSServerFactoryBean;
-import org.apache.cxf.jaxrs.openapi.OpenApiCustomizer;
 import org.apache.cxf.jaxrs.openapi.OpenApiFeature;
 import org.apache.cxf.jaxrs.security.SimpleAuthorizingFilter;
 import org.apache.cxf.jaxrs.swagger.ui.SwaggerUiConfig;
 import org.apache.cxf.message.Message;
 import org.apache.cxf.rs.security.cors.CrossOriginResourceSharingFilter;
+import dev.inoyu.openapi.enrich.cxf.BundleContextDocPackLoader;
+import dev.inoyu.openapi.enrich.cxf.EnrichingOpenApiCustomizer;
+import dev.inoyu.openapi.enrich.model.DocPack;
 import org.apache.unomi.api.ContextRequest;
 import org.apache.unomi.api.EventsCollectorRequest;
 import org.apache.unomi.api.security.SecurityService;
@@ -430,7 +432,8 @@ public class RestServer {
         openApiFeature.setUseContextBasedConfig(true);
         SwaggerUiConfig swaggerUiConfig = new SwaggerUiConfig().url("openapi.json").deepLinking(true).queryConfigEnabled(false);
         openApiFeature.setSwaggerUiConfig(swaggerUiConfig);
-        OpenApiCustomizer customizer = new OpenApiCustomizer();
+        DocPack openApiDocPack = new BundleContextDocPackLoader().loadMerged(bundleContext);
+        EnrichingOpenApiCustomizer customizer = new EnrichingOpenApiCustomizer(openApiDocPack);
         customizer.setDynamicBasePath(true);
         openApiFeature.setCustomizer(customizer);
         jaxrsServerFactoryBean.getFeatures().add(openApiFeature);
diff --git a/rest/src/main/java/org/apache/unomi/rest/tenants/TenantEndpoint.java b/rest/src/main/java/org/apache/unomi/rest/tenants/TenantEndpoint.java
index 3a61a09b7..26ada07f1 100644
--- a/rest/src/main/java/org/apache/unomi/rest/tenants/TenantEndpoint.java
+++ b/rest/src/main/java/org/apache/unomi/rest/tenants/TenantEndpoint.java
@@ -38,7 +38,8 @@ import java.util.List;
 /**
  * REST endpoint for managing tenants in the Apache Unomi system.
  * Provides operations for creating, updating, deleting, and retrieving tenants,
- * as well as managing their API keys and configurations.
+ * as well as managing their API keys, usage, and event retention.
+ * Class-level access requires the administrator role; usage and purge also accept tenant administrators.
  */
 @Produces(MediaType.APPLICATION_JSON)
 @CrossOriginResourceSharing(
@@ -59,7 +60,9 @@ public class TenantEndpoint {
     /**
      * Retrieves all tenants in the system.
      *
-     * @return a list of all tenants
+     * @return a JSON array of all tenants
+     * @api.status 200 array org.apache.unomi.api.tenants.Tenant List of tenants (may be empty).
+     * @api.example [{"itemId":"acme","itemType":"tenant","name":"Acme Corp","status":"ACTIVE","creationDate":"2026-01-15T10:00:00.000Z","apiKeys":[],"properties":{},"restrictedEventTypes":[],"authorizedIPs":[]}]
      */
     @GET
     @Produces(MediaType.APPLICATION_JSON)
@@ -72,6 +75,9 @@ public class TenantEndpoint {
      *
      * @param tenantId the ID of the tenant to retrieve
      * @return the requested tenant with 200 status, or 404 if tenant is not found
+     * @api.status 200 org.apache.unomi.api.tenants.Tenant Tenant found.
+     * @api.status 404 empty Tenant not found.
+     * @api.example {"itemId":"acme","itemType":"tenant","name":"Acme Corp","status":"ACTIVE","creationDate":"2026-01-15T10:00:00.000Z","lastModificationDate":"2026-01-15T10:00:00.000Z","apiKeys":[{"itemId":"key-1","itemType":"apiKey","maskedKey":"unomi_v1_****ab12","keyType":"PUBLIC","creationDate":"2026-01-15T10:00:00.000Z","revoked":false}],"properties":{},"restrictedEventTypes":[],"authorizedIPs":["10.0.0.0/8"]}
      */
     @GET
     @Path("/{tenantId}")
@@ -87,10 +93,17 @@ public class TenantEndpoint {
 
     /**
      * Creates a new tenant.
+     * On success, Unomi generates both a PUBLIC and a PRIVATE API key for the tenant.
+     * Plaintext keys are only returned in responses that explicitly expose
+     * {@link ApiKeyCreationResult} (not on this create response — the returned
+     * {@link Tenant} includes hashed/masked key metadata only).
      *
      * @param request the tenant creation request containing tenant details
-     * @return the created tenant with generated API keys
+     * @return the created tenant with generated API key metadata
      * @throws WebApplicationException with 400 status if request is invalid
+     * @api.status 200 org.apache.unomi.api.tenants.Tenant Tenant created (status ACTIVE, public and private keys generated).
+     * @api.status 400 empty Missing or blank requestedId.
+     * @api.example {"itemId":"acme","itemType":"tenant","status":"ACTIVE","creationDate":"2026-01-15T10:00:00.000Z","apiKeys":[{"maskedKey":"unomi_v1_****ab12","keyType":"PUBLIC","revoked":false},{"maskedKey":"unomi_v1_****cd34","keyType":"PRIVATE","revoked":false}],"properties":{"name":"Acme"},"restrictedEventTypes":[],"authorizedIPs":[]}
      */
     @POST
     @Consumes(MediaType.APPLICATION_JSON)
@@ -107,11 +120,16 @@ public class TenantEndpoint {
 
     /**
      * Updates an existing tenant.
+     * The path {@code tenantId} must equal {@code tenant.itemId}.
      *
      * @param tenantId the ID of the tenant to update
      * @param tenant the updated tenant information
      * @return the updated tenant
-     * @throws WebApplicationException with 404 status if tenant is not found
+     * @throws WebApplicationException with 400 if IDs mismatch, 404 if tenant is not found
+     * @api.status 200 org.apache.unomi.api.tenants.Tenant Tenant updated.
+     * @api.status 400 empty Path tenantId does not match body itemId.
+     * @api.status 404 empty Tenant not found.
+     * @api.example {"itemId":"acme","itemType":"tenant","name":"Acme Corp","status":"ACTIVE","creationDate":"2026-01-15T10:00:00.000Z","lastModificationDate":"2026-01-15T10:00:00.000Z","apiKeys":[{"itemId":"key-1","itemType":"apiKey","maskedKey":"unomi_v1_****ab12","keyType":"PUBLIC","creationDate":"2026-01-15T10:00:00.000Z","revoked":false}],"properties":{},"restrictedEventTypes":[],"authorizedIPs":["10.0.0.0/8"]}
      */
     @PUT
     @Path("/{tenantId}")
@@ -137,6 +155,9 @@ public class TenantEndpoint {
      * @param tenantId the ID of the tenant to delete
      * @return 204 No Content on success
      * @throws WebApplicationException with 404 status if tenant is not found
+     * @api.status 204 empty Tenant deleted.
+     * @api.status 404 empty Tenant not found.
+     * @api.example {}
      */
     @DELETE
     @Path("/{tenantId}")
@@ -152,12 +173,17 @@ public class TenantEndpoint {
 
     /**
      * Generates a new API key for a tenant.
+     * Replaces any existing key of the same {@code type}. The plaintext key is returned
+     * only in this response ({@link ApiKeyCreationResult#getPlainTextKey()}).
      *
      * @param tenantId the ID of the tenant
-     * @param type the type of API key to generate (PUBLIC or PRIVATE)
-     * @param validityDays the validity period in days (0 or null for no expiration)
+     * @param type the type of API key to generate ({@code PUBLIC} or {@code PRIVATE})
+     * @param validityDays the validity period in days ({@code 0} or omitted for no expiration)
      * @return the generated key metadata together with its one-time plaintext value
      * @throws WebApplicationException with 404 status if tenant is not found
+     * @api.status 200 org.apache.unomi.api.tenants.ApiKeyCreationResult Key created; store plainTextKey immediately.
+     * @api.status 404 empty Tenant not found.
+     * @api.example {"apiKey":{"itemId":"key-1","itemType":"apiKey","maskedKey":"unomi_v1_****ab12","keyType":"PUBLIC","creationDate":"2026-01-15T10:00:00.000Z","revoked":false},"plainTextKey":"unomi_v1_0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF"}
      */
     @POST
     @Path("/{tenantId}/apikeys")
@@ -185,9 +211,12 @@ public class TenantEndpoint {
      * Validates an API key for a tenant.
      *
      * @param tenantId the ID of the tenant
-     * @param apiKey the API key to validate
-     * @param type the type of API key (PUBLIC or PRIVATE)
+     * @param apiKey the plaintext API key to validate
+     * @param type the type of API key ({@code PUBLIC} or {@code PRIVATE})
      * @return 200 OK if valid, 401 Unauthorized if invalid
+     * @api.status 200 empty API key is valid for the tenant and type.
+     * @api.status 401 empty API key is missing, wrong type, revoked, expired, or unknown.
+     * @api.example {}
      */
     @GET
     @Path("/{tenantId}/apikeys/validate")
@@ -208,8 +237,12 @@ public class TenantEndpoint {
      * this endpoint exposes measured usage for operators and control planes.
      *
      * @param tenantId tenant identifier
-     * @param period reporting window: {@code current-month}, {@code YYYY-MM}, or legacy {@code 24h}
+     * @param period reporting window: {@code current-month} (default), {@code YYYY-MM}, or legacy {@code 24h}
      * @return usage snapshot, 404 if tenant is missing, 400 for unsupported period
+     * @api.status 200 org.apache.unomi.api.tenants.TenantUsage Usage snapshot (may be slightly stale until the next collection cycle).
+     * @api.status 400 empty Unsupported period value.
+     * @api.status 404 empty Tenant not found.
+     * @api.example {"tenantId":"acme","period":"2026-07","periodStart":1751328000000,"periodEnd":1754006400000,"profileCount":1200,"eventCount":45000,"scopeCount":3,"segmentCount":12,"ruleCount":8,"storageDocumentCount":52000,"activeApiKeyCount":2,"restRequestCount":180,"scopeUsages":[{"scopeId":"site","segmentCount":4,"ruleCount":3}],"collectedAt":1754000000000}
      */
     @GET
     @Path("/{tenantId}/usage")
@@ -232,11 +265,17 @@ public class TenantEndpoint {
     /**
      * Deletes events for the tenant at least {@code retentionDays} days old.
      * Destructive operation; intended for upstream retention jobs.
+     * Minimum retention is {@link TenantUsageService#MIN_EVENT_RETENTION_DAYS} days.
      *
      * @param tenantId tenant identifier
      * @param retentionDays age cutoff in whole days (minimum {@link TenantUsageService#MIN_EVENT_RETENTION_DAYS})
      * @return purge summary, 404 if tenant is missing, 400 for invalid retention, 500 if the
      *         persistence layer failed to complete the deletion (see {@code purgeRequested})
+     * @api.status 200 org.apache.unomi.api.tenants.TenantEventPurgeResult Delete-by-query completed ({@code purgeRequested}=true).
+     * @api.status 400 empty retentionDays missing, not positive, or below the configured minimum.
+     * @api.status 404 empty Tenant not found.
+     * @api.status 500 org.apache.unomi.api.tenants.TenantEventPurgeResult Persistence accepted the request but deletion failed ({@code purgeRequested}=false).
+     * @api.example {"tenantId":"acme","retentionDays":30,"eventsMatched":1200,"purgeRequested":true,"requestedAt":1754000000000}
      */
     @POST
     @Path("/{tenantId}/purge/events")
diff --git a/rest/src/main/java/org/apache/unomi/rest/tenants/TenantRequest.java b/rest/src/main/java/org/apache/unomi/rest/tenants/TenantRequest.java
index cddc93b4e..8ec93db62 100644
--- a/rest/src/main/java/org/apache/unomi/rest/tenants/TenantRequest.java
+++ b/rest/src/main/java/org/apache/unomi/rest/tenants/TenantRequest.java
@@ -19,12 +19,20 @@ package org.apache.unomi.rest.tenants;
 import java.util.Map;
 
 /**
- * REST request payload for creating or updating a {@link org.apache.unomi.api.tenants.Tenant}.
- * Carries the desired tenant id and an open properties map that maps to
- * tenant configuration fields accepted by {@link org.apache.unomi.api.tenants.TenantService}.
+ * REST request body for creating a {@link org.apache.unomi.api.tenants.Tenant}.
+ * Carries the desired tenant id and an open properties map stored on the tenant.
  */
 public class TenantRequest {
+    /**
+     * Desired tenant identifier ({@link org.apache.unomi.api.Item#itemId}). Required; blank values yield HTTP 400.
+     * @api.example acme
+     */
     private String requestedId;
+    /**
+     * Optional open map of custom tenant properties persisted as {@code Tenant.properties}.
+     * Free-form keys; common ones include display hints such as {@code name} or {@code description}.
+     * @api.example {"name":"Acme Corp","description":"Production tenant"}
+     */
     private Map<String, Object> properties;
 
     /**
@@ -62,4 +70,4 @@ public class TenantRequest {
     public void setProperties(Map<String, Object> properties) {
         this.properties = properties;
     }
-}
\ No newline at end of file
+}
diff --git a/tracing/tracing-api/src/main/java/org/apache/unomi/tracing/api/TraceNode.java b/tracing/tracing-api/src/main/java/org/apache/unomi/tracing/api/TraceNode.java
index 683d75bc8..9c11fec8d 100644
--- a/tracing/tracing-api/src/main/java/org/apache/unomi/tracing/api/TraceNode.java
+++ b/tracing/tracing-api/src/main/java/org/apache/unomi/tracing/api/TraceNode.java
@@ -31,13 +31,44 @@ import java.util.List;
  */
 @JsonIgnoreProperties(ignoreUnknown = true)
 public class TraceNode implements Serializable {
+    /**
+     * Type of operation represented by this node.
+     * @api.example event-collection
+     */
     private String operationType;
+    /**
+     * Human-readable description of this operation.
+     * @api.example Processing event collection request
+     */
     private String description;
+    /**
+     * Additional context information for this operation.
+     * @api.example scope=mysite
+     */
     private String context;
+    /**
+     * Result summary for this operation.
+     * @api.example updated=6
+     */
     private String result;
+    /**
+     * Start time of this operation in milliseconds since epoch.
+     * @api.example 1718448600000
+     */
     private long startTime;
+    /**
+     * End time of this operation in milliseconds since epoch.
+     * @api.example 1718448600125
+     */
     private long endTime;
+    /**
+     * Trace messages recorded for this node.
+     * @api.example ["started","rules-evaluated"]
+     */
     private List<String> traces;
+    /**
+     * Nested child trace nodes.
+     */
     private List<TraceNode> children;
 
     /**
@@ -160,6 +191,7 @@ public class TraceNode implements Serializable {
      * Returns the operation duration in milliseconds.
      *
      * @return duration as {@code endTime - startTime}, or zero if end time precedes start time
+     * @api.example 125
      */
     public long getDuration() {
         return Math.max(0, endTime - startTime);