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:07 UTC

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

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);