diff --git a/Runtime/Scripts/WorldAnalysisREST.cs b/Runtime/Scripts/WorldAnalysisREST.cs
index ca881ccf6ac770dcc857c323484b98e8d2e0249b..439871247d7de73707a3a813881c61749c5c6526 100644
--- a/Runtime/Scripts/WorldAnalysisREST.cs
+++ b/Runtime/Scripts/WorldAnalysisREST.cs
@@ -1,23 +1,54 @@
 using System;
+using System.Collections;
+using System.Collections.Generic;
 using UnityEngine;
 using ETSI.ARF.OpenAPI.WorldAnalysis;
 using ETSI.ARF.WorldAnalysis;
 using static WorldAnalysisInterface;
 using ETSI.ARF.WorldAnalysis.REST;
 
+using WebSocketSharp;
+
 //Implementation of the WorldAnalysis interface
 public class WorldAnalysisREST : MonoBehaviour, WorldAnalysisInterface
 {
-    static protected string token = "ARF_Permission";
-
+    //
+    // Inspector variables
+    //
+    /// <summary>
+    /// WorldSAnalysisServer
+    /// </summary>
     public WorldAnalysisServer waServer;
+    public string token = "ETSI-ARF-STF";
+    public string sessionID = "RESTful-API";
 
-    // For sync calls
-    private WorldAnalysisClient apiClient;
+    [Space(8)]
+    public bool isDebug = false;
 
-    // For async calls
-    private WorldAnalysisClient apiClientAsync;
+    //
+    // Private members
+    //
+    private WorldAnalysisClient apiClient;          // For sync calls
+    private WorldAnalysisClient apiClientAsync;     // For async calls
+    private WebSocketSharp.WebSocket webSocket;     // For WebSockets
+    private bool websocketConnected = false;
 
+    //
+    // Management of subscriptions
+    //
+    /// <summary>
+    /// Dictionnary of susbscription informations for poses, for each item, stored using the UUID of the item (anchor/trackable)
+    /// </summary>
+    private Dictionary<Guid, SubscriptionInfo> m_subscriptionsPoses;
+
+    public struct SubscriptionInfo
+    {
+        public Guid uuid;           // id of subscription (id is defined by the WA server)
+        public Guid uuidTarget;     // id trackable or anchor
+        public float timeValidity;  //The duration of the validity of the subscription
+        public ETSI.ARF.OpenAPI.WorldAnalysis.Pose pose;
+        public PoseCallback callback;
+    }
 
     #region Unity_Methods
 
@@ -26,6 +57,11 @@ public class WorldAnalysisREST : MonoBehaviour, WorldAnalysisInterface
     /// </summary>
     protected void Awake()
     {
+        Instance = this;
+        //m_relocalizationInformations = new Dictionary<Guid, ETSI.ARF.OpenAPI.WorldStorage.RelocalizationInformation>();
+        //m_computedPoses = new Dictionary<Guid, ETSI.ARF.OpenAPI.WorldAnalysis.Pose>(); 
+        m_subscriptionsPoses = new Dictionary<Guid, SubscriptionInfo>();
+
         // sync
         var httpClient = new BasicHTTPClient(waServer.URI);
         apiClient = new WorldAnalysisClient(httpClient);
@@ -47,6 +83,12 @@ public class WorldAnalysisREST : MonoBehaviour, WorldAnalysisInterface
     /// </summary>
     protected void Update()
     {
+        ManageSubscriptionValidity();
+
+        // todo: Call subscription callback(s) here or in the websocket?!?
+        foreach (KeyValuePair<Guid, SubscriptionInfo> subPose in m_subscriptionsPoses)
+        {
+        }
     }
 
     #endregion
@@ -62,29 +104,130 @@ public class WorldAnalysisREST : MonoBehaviour, WorldAnalysisInterface
         Debug.Log("[REST] WA Version: " + ver);
     }
 
-    public string GetWebSocketEndpoint()
+    public void PrintCapabilities(Capability[] capabilities)
+    {
+        string res = "";
+        Debug.Log("[REST] Got " + capabilities.Length + " capabilities.");
+        foreach (var item in capabilities)
+        {
+            res += "\nCapability: " + item.TrackableType + " Version: " + item.EncodingInformation.Version;
+        }
+        res += "\nEnd of capabilities.";
+        Debug.Log("[REST] Capabilities: " + res);
+    }
+    #endregion
+
+    #region Communication system
+    private void CreateWebHookServer()
+    {
+        throw new Exception("[REST] CreateWebHookServer(): Not implemented!");
+    }
+
+    private void DestroyWebHookServer()
+    {
+        return;
+    }
+
+    public WebSocket OpenWebSocketClient(string url)
     {
-        string res = "empty";
+        webSocket = new WebSocketSharp.WebSocket(url);
+
+        //
+        // Define standard callbacks
+        //
+        webSocket.OnOpen += (sender, e) =>
+        {
+            Debug.Log("[WS] Connected");
+            websocketConnected = true;
+            webSocket.Send("RegisterClient:UnitySceneManagement");
+        };
+        webSocket.OnClose += (sender, e) =>
+        {
+            Debug.Log("[WS] Disconnected");
+            websocketConnected = false;
+        };
+        webSocket.OnError += (sender, e) => Debug.Log("[WS] Error!");
+        webSocket.OnMessage += (sender, e) => HandleWebSocketClient(e.Data);
+        webSocket.Connect();
 
-        SubscriptionSingleRequest param = new SubscriptionSingleRequest();
-        param.Mode = Mode_WorldAnalysis.DEVICE_TO_TRACKABLES;
-        param.Target = Guid.Parse("fa8bbe40-8052-11ec-a8a3-0242ac120002"); // test
+        return webSocket;
+    }
 
-        SubscriptionSingle response = apiClient.SubscribeToPose(token, "1", param);
-        res = response.WebsocketUrl;
-        return res;
-    } 
-    
-    public void PrintCapabilities()
+    private void OnDestroy()
     {
-        string res = "Capabilities:";
+        // State: red
+        CloseWebSocketClient();
+    }
 
-        Response2 cap = apiClient.GetCapabilities(token, "1");
-        foreach (var item in cap.Capabilities)
+    private void CloseWebSocketClient()
+    {
+        if (websocketConnected)
         {
-            res += "\n" + item.TrackableType;
+            webSocket.Send("UnregisterClient");
+            webSocket.Close();
+        }
+    }
+
+    bool ok = false;
+    public void HandleWebSocketClient(string data)
+    {
+        Debug.Log("[WS] Receiving: " + data);
+
+        if (data.Contains("You are now registered"))
+        {
+            ok = true;
+            if (isDebug) webSocket.Send("PoseStart:10"); // test
+        }
+        else if (data == "PoseStop")
+        {
+            //SetColor(Color.yellow);
+        }
+        else if (ok)
+        {
+            if (data.Contains("estimationState"))
+            {
+                // Handle pose
+                ETSI.ARF.OpenAPI.WorldAnalysis.Pose p = JsonUtility.FromJson<ETSI.ARF.OpenAPI.WorldAnalysis.Pose>(data);
+                Debug.Log("[WS][Pose] State: " + p.EstimationState.ToString());
+
+                PoseEstimationResult res = p.EstimationState == PoseEstimationState.OK ? PoseEstimationResult.OK : PoseEstimationResult.FAILURE;
+
+                // Search the corresponding callbacks
+                foreach (var item in m_subscriptionsPoses.Values)
+                {
+                    if (p.Uuid == item.uuidTarget)
+                    {
+                        item.callback(res, p);
+                    }
+                }
+            }
+        }
+    }
+
+    #endregion
+
+    #region Lifecycle
+    /// <summary>
+    /// Check the validity of all subscriptions and delete one if needed
+    /// </summary>
+    protected void ManageSubscriptionValidity()
+    {
+        if (m_subscriptionsPoses.Count == 0) return;
+
+        List<Guid> subscriptionToDelete = new List<Guid>();
+        foreach (KeyValuePair<Guid, SubscriptionInfo> sub in m_subscriptionsPoses)
+        {
+            float validity = sub.Value.timeValidity;
+            if (Time.time > validity)
+            {
+                subscriptionToDelete.Add(sub.Key);
+            }
+        }
+        foreach (Guid s in subscriptionToDelete)
+        {
+            Debug.Log("ETSI ARF : Subscription deleted " + s);
+            m_subscriptionsPoses.Remove(s);
         }
-        Debug.Log("[REST] Capabilities: " + res);
     }
     #endregion
 
@@ -94,57 +237,245 @@ public class WorldAnalysisREST : MonoBehaviour, WorldAnalysisInterface
     //
     public AskFrameRateResult SetPoseEstimationFramerate(string token, PoseConfigurationTrackableType type, EncodingInformationStructure encodingInformation, int minimumFramerate)
     {
-        return AskFrameRateResult.NOT_SUPPORTED; ///We cannot set any framerate for tracking on ARKit and ARCore
+        PoseConfiguration poseConfig = new PoseConfiguration();
+        poseConfig.TrackableType = type;
+        poseConfig.EncodingInformation = encodingInformation;
+        poseConfig.Framerate = minimumFramerate;
+        apiClient.ConfigureFramerate(token, sessionID, poseConfig);
+        return AskFrameRateResult.OK;
     }
 
     public PoseEstimationResult GetLastPose(string token, Guid uuid, Mode_WorldAnalysis mode, out ETSI.ARF.OpenAPI.WorldAnalysis.Pose pose)
     {
-        pose = null;
-        return PoseEstimationResult.OK;
+        pose = apiClient.GetPose(token, sessionID, uuid, mode);
+        return pose != null ? PoseEstimationResult.OK : PoseEstimationResult.NOT_SUPPORTED;
     }
 
 
     public PoseEstimationResult[] GetLastPoses(string token, Guid[] uuids, Mode_WorldAnalysis[] modes, out ETSI.ARF.OpenAPI.WorldAnalysis.Pose[] poses)
     {
-        poses = null;
-        return null;
+        if (uuids.Length != modes.Length)
+        {
+            Debug.LogError("[REST] ETSI ARF: Get poses: uuids and modes array do no have the same length");
+            poses = null;
+            return null;
+        }
+        PoseEstimationResult[] resul = new PoseEstimationResult[uuids.Length];
+        poses = new ETSI.ARF.OpenAPI.WorldAnalysis.Pose[uuids.Length];
+        List<Anonymous> uuidList = new List<Anonymous>();
+        Response poses_ = apiClient.GetPoses(token, sessionID, uuidList.ToArray());
+        List<ETSI.ARF.OpenAPI.WorldAnalysis.Pose> posesList = poses_.Poses as List<ETSI.ARF.OpenAPI.WorldAnalysis.Pose>;
+
+        if (poses_ != null && posesList != null && posesList.Count > 0)
+        {
+            for (int i = 0; i < uuids.Length; i++)
+            {
+                PoseEstimationResult poseResul = new PoseEstimationResult();
+                resul[i] = poseResul;
+                poses[i] = posesList[i];
+            }
+            return resul;
+        }
+        else
+        {
+            poses = null;
+            return null;
+        }
     }
 
     public InformationSubscriptionResult SubscribeToPose(string token, Guid uuid, Mode_WorldAnalysis mode, PoseCallback callback, ref int validity, out Guid subscriptionUUID)
     {
-        subscriptionUUID = Guid.Empty;
+        // Todo: Maintain the callback to the subscription id
+        // Get capabilities?
+        // Get reloc info?
+
+        SubscriptionSingleRequest body = new SubscriptionSingleRequest();
+        body.Target = uuid;
+        body.Mode = mode;
+        body.Validity = validity;
+        body.WebhookUrl = callback == null ? "" : "https:\\..."; // empty -> app will use websockets (client)!
+
+        // Get subscription info from the REST server
+        SubscriptionSingle response = apiClient.SubscribeToPose(token, sessionID, body);
+        subscriptionUUID = response.Uuid;
+
+        // We add the subscription
+        SubscriptionInfo sub = new SubscriptionInfo();
+        sub.uuid = response.Uuid;
+        sub.timeValidity = Time.time + (validity / 1000.0f);
+        sub.pose = new ETSI.ARF.OpenAPI.WorldAnalysis.Pose();
+        sub.pose.Mode = mode;
+        sub.uuidTarget = uuid;
+        sub.callback = callback;
+        m_subscriptionsPoses.Add(sub.uuid, sub);
+
+        if (!string.IsNullOrEmpty(response.WebhookUrl))
+        {
+            CloseWebSocketClient();
+
+            // todo: create a REST server so that the WA server can send pose update to it
+            // How to auto-generate the C# REST server for pose for Unity?
+            CreateWebHookServer();
+        }
+        else
+        {
+            DestroyWebHookServer();
+
+            // todo: Open the websocket?
+            string websocketUrl = response.WebsocketUrl;
+            if (isDebug) websocketUrl = "ws://localhost:61788/ws"; // for tests
+
+            if (string.IsNullOrEmpty(websocketUrl))
+            {
+                // Create the WebSockets client here (NOT in the scene scripts)
+                if (!websocketConnected) OpenWebSocketClient(websocketUrl);
+            }
+            else throw new Exception("[REST] No valid WebSockets URL in server reponse.");
+        }
         return InformationSubscriptionResult.OK;
     }
 
     public InformationSubscriptionResult[] SubscribeToPoses(string token, Guid[] uuids, Mode_WorldAnalysis[] modes, PoseCallback callback, ref int validity, out Guid[] subscriptionUUIDs)
     {
-        subscriptionUUIDs = null;
-        return null;
+        if (uuids.Length != 0 && uuids.Length == modes.Length)
+        {
+            InformationSubscriptionResult[] resul = new InformationSubscriptionResult[uuids.Length];
+            subscriptionUUIDs = new Guid[uuids.Length];
+            for (int i = 0; i < uuids.Length; i++)
+            {
+                resul[i] = SubscribeToPose(token, uuids[i], modes[i], callback, ref validity, out subscriptionUUIDs[i]);
+            }
+            return resul;
+        }
+        else
+        {
+            Debug.LogError("[REST] ETSI ARF: Subscribe poses: uuids and modes array do no have the same length");
+            subscriptionUUIDs = null;
+            return null;
+        }
     }
 
     public InformationSubscriptionResult GetSubsription(string token, Guid subscriptionUUID, out PoseCallback callback, out Guid target, out Mode_WorldAnalysis mode, out int validity)
     {
+        // default
         callback = null;
         target = Guid.Empty;
         mode = Mode_WorldAnalysis.TRACKABLES_TO_DEVICE;
         validity = 0;
-        return InformationSubscriptionResult.OK;
+
+        if (m_subscriptionsPoses.ContainsKey(subscriptionUUID))
+        {
+            // Check the server subscription
+            SubscriptionSingle sub = apiClient.GetSubscription(token, sessionID, subscriptionUUID);
+
+            // Check local one
+            if (sub.Uuid == subscriptionUUID)
+            {
+                SubscriptionInfo subInfo = m_subscriptionsPoses[subscriptionUUID];
+                callback = subInfo.callback;
+                target = subInfo.uuidTarget;
+                mode = subInfo.pose.Mode;
+                float validitySeconds = subInfo.timeValidity - Time.time;
+                validity = (int)validitySeconds * 1000;// conversion in ms
+
+                // Compare both
+                if (target == sub.Target && mode == sub.Mode && validity == sub.Validity)
+                    return InformationSubscriptionResult.OK;
+                else
+                    return InformationSubscriptionResult.NOT_ALLOWED;
+            }
+        }
+        return InformationSubscriptionResult.UNKNOWN_ID;
     }
 
     public InformationSubscriptionResult UpdateSubscription(string token, Guid subscriptionUUID, Mode_WorldAnalysis mode, int validity, PoseCallback callback)
     {
-        return InformationSubscriptionResult.OK;
+        // default
+        callback = null;
+        mode = Mode_WorldAnalysis.TRACKABLES_TO_DEVICE;
+        validity = 0;
+
+        if (m_subscriptionsPoses.ContainsKey(subscriptionUUID))
+        {
+            SubscriptionInfo sub = m_subscriptionsPoses[subscriptionUUID];
+            PoseCallback oldCB = sub.callback;
+
+            Body body = new Body();
+            body.Mode = mode;
+            body.Validity = validity;
+            body.WebhookUrl = callback == null ? "" : "https:\\..."; // empty -> app will use websockets (client)!
+
+            // Update subscription info in the REST server
+            SubscriptionSingle response = apiClient.UpdateSubscription(token, sessionID, subscriptionUUID, body);
+
+            // Update local data
+            sub.pose.Mode = response.Mode;
+            sub.callback = callback;
+            sub.timeValidity = Time.time + (validity / 1000.0f);
+
+            //
+            // Recreate server/connection to ws only if someone changed!
+            //
+            if (oldCB != null && callback == null && !string.IsNullOrEmpty(response.WebhookUrl))
+            {
+                CloseWebSocketClient();
+
+                // todo: create a REST server so that the WA server can send pose update to it
+                // How to auto-generate the C# REST server for pose for Unity?
+                CreateWebHookServer();
+            }
+            else if (oldCB == null && callback != null && string.IsNullOrEmpty(response.WebhookUrl))
+            {
+                DestroyWebHookServer();
+
+                // todo: Open the websocket?
+                string websocketUrl = response.WebsocketUrl;
+                if (isDebug) websocketUrl = "ws://localhost:61788/ws"; // for tests
+
+                if (string.IsNullOrEmpty(websocketUrl))
+                {
+                    // Create the WebSockets client here (NOT in the scene scripts)
+                    if (!websocketConnected) OpenWebSocketClient(websocketUrl);
+                }
+                else throw new Exception("[REST] No valid WebSockets URL in server reponse.");
+            }
+
+            return InformationSubscriptionResult.OK;
+        }
+        return InformationSubscriptionResult.UNKNOWN_ID;
     }
 
     public InformationSubscriptionResult UnSubscribeToPose(Guid subscriptionUUID)
     {
-        return InformationSubscriptionResult.OK;
+        if (m_subscriptionsPoses.ContainsKey(subscriptionUUID))
+        {
+            apiClient.UnsubscribeFromPose(token, sessionID, subscriptionUUID);
+            m_subscriptionsPoses.Remove(subscriptionUUID);
+
+            if (m_subscriptionsPoses.Count == 0)
+            {
+                // Close the connection via websockets
+                CloseWebSocketClient();
+            }
+            return InformationSubscriptionResult.OK;
+        }
+        return InformationSubscriptionResult.UNKNOWN_ID;
     }
 
     public CapabilityResult GetCapabilities(string token, out Capability[] capabilities)
     {
-        capabilities = null;
-        return CapabilityResult.OK;
+        Response2 cap = apiClient.GetCapabilities(token, sessionID);
+        if (cap == null || cap.Capabilities == null || cap.Capabilities.Count == 0)
+        {
+            capabilities = null;
+            return CapabilityResult.FAIL;
+        }
+        else
+        {
+            capabilities = new Capability[cap.Capabilities.Count];
+            cap.Capabilities.CopyTo(capabilities, 0);
+            return CapabilityResult.OK;
+        }
     }
 
     public CapabilityResult GetCapability(string token, Guid uuid, out bool isSupported, out TypeWorldStorage type, out Capability[] capability)
@@ -152,7 +483,21 @@ public class WorldAnalysisREST : MonoBehaviour, WorldAnalysisInterface
         isSupported = false;
         type = TypeWorldStorage.UNKNOWN;
         capability = null;
-        return CapabilityResult.OK;
+
+        Response3 cap = apiClient.GetSupport(token, sessionID, uuid);
+        if (cap == null || cap.Capabilities == null || cap.Capabilities.Count == 0)
+        {
+            isSupported = false;
+            capability = null;
+            return CapabilityResult.FAIL;
+        }
+        else
+        {
+            isSupported = true;
+            capability = new Capability[cap.Capabilities.Count];
+            cap.Capabilities.CopyTo(capability, 0);
+            return CapabilityResult.OK;
+        }
     }
 
     #endregion