diff --git a/.github/workflows/compile-examples.yml b/.github/workflows/compile-examples.yml
index 5eb4f66d9..d30277a07 100644
--- a/.github/workflows/compile-examples.yml
+++ b/.github/workflows/compile-examples.yml
@@ -194,7 +194,11 @@ jobs:
               - name: ArduinoECCX08
               - name: Arduino_Cellular
               - name: Blues Wireless Notecard
+              - name: ArduinoBLE
+              - name: Arduino_KVStore
+              - name: Arduino_NetworkConfigurator
             sketch-paths: |
+              - examples/ArduinoIoTCloud-NetConfig
               - examples/ArduinoIoTCloud-DeferredOTA
               - examples/ArduinoIoTCloud-Notecard
               - examples/ArduinoIoTCloud-Schedule
@@ -207,7 +211,11 @@ jobs:
               - name: arduino:mbed_nicla
             libraries: |
               - name: Blues Wireless Notecard
+              - name: ArduinoBLE
+              - name: Arduino_KVStore
+              - name: Arduino_NetworkConfigurator
             sketch-paths: |
+              - examples/ArduinoIoTCloud-NetConfig
               - examples/ArduinoIoTCloud-DeferredOTA
               - examples/ArduinoIoTCloud-Notecard
               - examples/ArduinoIoTCloud-Schedule
@@ -222,7 +230,11 @@ jobs:
               - name: ArduinoBearSSL
               - name: ArduinoECCX08
               - name: Blues Wireless Notecard
+              - name: ArduinoBLE
+              - name: Arduino_KVStore
+              - name: Arduino_NetworkConfigurator
             sketch-paths: |
+              - examples/ArduinoIoTCloud-NetConfig
               - examples/ArduinoIoTCloud-DeferredOTA
               - examples/ArduinoIoTCloud-Notecard
               - examples/ArduinoIoTCloud-Schedule
@@ -237,7 +249,11 @@ jobs:
               - name: ArduinoBearSSL
               - name: ArduinoECCX08
               - name: Blues Wireless Notecard
+              - name: ArduinoBLE
+              - name: Arduino_KVStore
+              - name: Arduino_NetworkConfigurator
             sketch-paths: |
+              - examples/ArduinoIoTCloud-NetConfig
               - examples/ArduinoIoTCloud-DeferredOTA
               - examples/ArduinoIoTCloud-Notecard
               - examples/ArduinoIoTCloud-Schedule
@@ -251,7 +267,11 @@ jobs:
             libraries: |
               - name: Arduino_Cellular
               - name: Blues Wireless Notecard
+              - name: ArduinoBLE
+              - name: Arduino_KVStore
+              - name: Arduino_NetworkConfigurator
             sketch-paths: |
+              - examples/ArduinoIoTCloud-NetConfig
               - examples/ArduinoIoTCloud-Notecard
               - examples/ArduinoIoTCloud-Schedule
               - examples/utility/Provisioning
@@ -263,7 +283,11 @@ jobs:
               - name: arduino:renesas_uno
             libraries: |
               - name: Blues Wireless Notecard
+              - name: ArduinoBLE
+              - name: Arduino_KVStore
+              - name: Arduino_NetworkConfigurator
             sketch-paths: |
+              - examples/ArduinoIoTCloud-NetConfig
               - examples/ArduinoIoTCloud-Notecard
               - examples/ArduinoIoTCloud-Schedule
           # Nano ESP32
diff --git a/examples/ArduinoIoTCloud-NetConfig/ArduinoIoTCloud-NetConfig.ino b/examples/ArduinoIoTCloud-NetConfig/ArduinoIoTCloud-NetConfig.ino
new file mode 100644
index 000000000..238b59f55
--- /dev/null
+++ b/examples/ArduinoIoTCloud-NetConfig/ArduinoIoTCloud-NetConfig.ino
@@ -0,0 +1,62 @@
+/*
+  This sketch demonstrates how to exchange data between your board and the Arduino IoT Cloud.
+
+  * Connect a potentiometer (or other analog sensor) to A0.
+  * When the potentiometer (or sensor) value changes the data is sent to the Cloud.
+  * When you flip the switch in the Cloud dashboard the onboard LED lights gets turned ON or OFF.
+
+  IMPORTANT:
+  This sketch works with WiFi, GSM, NB, Ethernet and Lora enabled boards supported by Arduino IoT Cloud.
+  On a LoRa board, if it is configured as a class A device (default and preferred option),
+  values from Cloud dashboard are received only after a value is sent to Cloud.
+
+  The full list of compatible boards can be found here:
+   - https://github.com/arduino-libraries/ArduinoIoTCloud#what
+*/
+
+#include "thingProperties.h"
+
+#if !defined(LED_BUILTIN) && !defined(ARDUINO_NANO_ESP32)
+static int const LED_BUILTIN = 2;
+#endif
+
+void setup() {
+  /* Initialize serial and wait up to 5 seconds for port to open */
+  Serial.begin(9600);
+  for(unsigned long const serialBeginTime = millis(); !Serial && (millis() - serialBeginTime <= 5000); ) { }
+
+  /* Set the debug message level:
+   * - DBG_ERROR: Only show error messages
+   * - DBG_WARNING: Show warning and error messages
+   * - DBG_INFO: Show info, warning, and error messages
+   * - DBG_DEBUG: Show debug, info, warning, and error messages
+   * - DBG_VERBOSE: Show all messages
+   */
+  setDebugMessageLevel(DBG_INFO);
+
+  /* Configure LED pin as an output */
+  pinMode(LED_BUILTIN, OUTPUT);
+
+  /* This function takes care of connecting your sketch variables to the ArduinoIoTCloud object */
+  initProperties();
+
+  /* Initialize Arduino IoT Cloud library */
+  ArduinoCloud.begin(ArduinoIoTPreferredConnection);
+
+  ArduinoCloud.printDebugInfo();
+}
+
+void loop() {
+  ArduinoCloud.update();
+  potentiometer = analogRead(A0);
+  seconds = millis() / 1000;
+}
+
+/*
+ * 'onLedChange' is called when the "led" property of your Thing changes
+ */
+void onLedChange() {
+  Serial.print("LED set to ");
+  Serial.println(led);
+  digitalWrite(LED_BUILTIN, led);
+}
diff --git a/examples/ArduinoIoTCloud-NetConfig/thingProperties.h b/examples/ArduinoIoTCloud-NetConfig/thingProperties.h
new file mode 100644
index 000000000..f10ba6178
--- /dev/null
+++ b/examples/ArduinoIoTCloud-NetConfig/thingProperties.h
@@ -0,0 +1,48 @@
+#if !defined(ARDUINO_SAMD_MKRWIFI1010) && !defined(ARDUINO_SAMD_NANO_33_IOT) &&  !defined(ARDUINO_NANO_RP2040_CONNECT) \
+  && !defined(ARDUINO_PORTENTA_H7_M7) && !defined(ARDUINO_NICLA_VISION) && !defined(ARDUINO_OPTA) && !defined(ARDUINO_GIGA) \
+  && !defined(ARDUINO_UNOR4_WIFI) && !defined(ARDUINO_PORTENTA_C33)
+#error "This example is not compatible with this board."
+#endif
+#include <ArduinoIoTCloud.h>
+#include <Arduino_ConnectionHandler.h>
+#include <GenericConnectionHandler.h>
+#include "configuratorAgents/agents/BLEAgent.h"
+#include "configuratorAgents/agents/SerialAgent.h"
+
+void onLedChange();
+
+bool led;
+int potentiometer;
+int seconds;
+
+GenericConnectionHandler ArduinoIoTPreferredConnection;
+KVStore kvStore;
+NetworkConfiguratorClass NetworkConfigurator(ArduinoIoTPreferredConnection);
+BLEAgentClass BLEAgent;
+SerialAgentClass SerialAgent;
+
+void initProperties() {
+  NetworkConfigurator.addAgent(BLEAgent);
+  NetworkConfigurator.addAgent(SerialAgent);
+  NetworkConfigurator.setStorage(kvStore);
+
+  /* For changing the default reset pin uncomment and set your preferred pin.
+   * Use DISABLE_PIN for disabling the reset procedure.
+   * The pin must be in the list of digital pins usable for interrupts.
+   * Please refer to the Arduino documentation for more details:
+   * https://docs.arduino.cc/language-reference/en/functions/external-interrupts/attachInterrupt/
+   */
+  //NetworkConfigurator.setReconfigurePin(your_pin);
+
+  /* Otherwise if you need to monitor the pin status changes
+   * you can set a custom callback function that is fired on every change
+   */
+  //NetworkConfigurator.setPinChangedCallback(your_callback);
+
+  ArduinoCloud.setConfigurator(NetworkConfigurator);
+
+  ArduinoCloud.addProperty(led, Permission::Write).onUpdate(onLedChange);
+  ArduinoCloud.addProperty(potentiometer, Permission::Read).publishOnChange(10);
+  ArduinoCloud.addProperty(seconds, Permission::Read).publishOnChange(1);
+
+}
diff --git a/examples/utility/Provisioning_2.0/CSRHandler.cpp b/examples/utility/Provisioning_2.0/CSRHandler.cpp
new file mode 100644
index 000000000..1a6101e4b
--- /dev/null
+++ b/examples/utility/Provisioning_2.0/CSRHandler.cpp
@@ -0,0 +1,428 @@
+/*
+  Copyright (c) 2024 Arduino SA
+
+  This Source Code Form is subject to the terms of the Mozilla Public
+  License, v. 2.0. If a copy of the MPL was not distributed with this
+  file, You can obtain one at http://mozilla.org/MPL/2.0/.
+*/
+
+#include "CSRHandler.h"
+#include <utility/SElementArduinoCloudCertificate.h>
+#include <utility/SElementCSR.h>
+#include <utility/SElementArduinoCloudDeviceId.h>
+#include <utility/SElementArduinoCloud.h>
+#include <utility/SElementArduinoCloudJWT.h>
+#include <utility/time/TimeService.h>
+#include <stdlib.h>
+#include "Arduino_DebugUtils.h"
+#include <Arduino_HEX.h>
+
+#define RESPONSE_TIMEOUT 5000
+#define CONNECTION_RETRY_TIMEOUT_MS 2000
+#define BACKEND_INTERVAL_s 12
+#define MAX_CSR_REQUEST_INTERVAL 180000
+#define MAX_CSR_REQUEST_INTERVAL_ATTEMPTS 15
+
+#ifdef COMPILE_TEST
+constexpr char *server = "boards-v2.oniudra.cc";
+#else
+constexpr char *server = "boards-v2.arduino.cc";
+#endif
+
+CSRHandlerClass::CSRHandlerClass() :
+  _ledFeedback{LEDFeedbackClass::getInstance()},
+  _state{CSRHandlerStates::END},
+  _nextRequestAt{0},
+  _requestAttempt{0},
+  _startWaitingResponse{0},
+  _uhwid{nullptr},
+  _certForCSR{nullptr},
+  _connectionHandler{nullptr},
+  _secureElement{nullptr},
+  _tlsClient{nullptr},
+  _client{nullptr},
+  _fw_version{""},
+  _deviceId{""},
+  _issueYear{0},
+  _issueMonth{0},
+  _issueDay{0},
+  _issueHour{0} {
+  memset(_serialNumber, 0, sizeof(_serialNumber));
+  memset(_authorityKeyIdentifier, 0, sizeof(_authorityKeyIdentifier));
+  memset(_signature, 0, sizeof(_signature));
+}
+
+CSRHandlerClass::~CSRHandlerClass() {
+  if (_certForCSR) {
+    delete _certForCSR;
+    _certForCSR = nullptr;
+  }
+  if (_client) {
+    delete _client;
+    _client = nullptr;
+  }
+
+  if(_tlsClient){
+    delete _tlsClient;
+    _tlsClient = nullptr;
+  }
+}
+
+bool CSRHandlerClass::begin(ConnectionHandler &connectionHandler, SecureElement &secureElement, String &uhwid) {
+  if(_state != CSRHandlerStates::END) {
+    return true;
+  }
+
+  if(uhwid == "") {
+    return false;
+  }
+
+  _connectionHandler = &connectionHandler;
+  _secureElement = &secureElement;
+  _uhwid = &uhwid;
+
+#ifdef BOARD_HAS_WIFI
+  _fw_version = WiFi.firmwareVersion();
+#endif
+  if(!_tlsClient){
+    _tlsClient = new TLSClientMqtt();
+  }
+  _tlsClient->begin(*_connectionHandler);
+  _tlsClient->setTimeout(RESPONSE_TIMEOUT);
+  _client = new HttpClient(*_tlsClient, server, 443);
+  TimeService.begin(_connectionHandler);
+  _requestAttempt = 0;
+  _nextRequestAt = 0;
+  _startWaitingResponse = 0;
+  _state = CSRHandlerStates::BUILD_CSR;
+}
+
+void CSRHandlerClass::end() {
+  if (_client) {
+    _client->stop();
+    delete _client;
+    _client = nullptr;
+  }
+
+  if (_certForCSR) {
+    delete _certForCSR;
+    _certForCSR = nullptr;
+  }
+
+  if(_tlsClient){
+    delete _tlsClient;
+    _tlsClient = nullptr;
+  }
+  _fw_version = "";
+  _deviceId = "";
+  _state = CSRHandlerStates::END;
+}
+
+CSRHandlerClass::CSRHandlerStates CSRHandlerClass::poll() {
+  switch (_state) {
+    case CSRHandlerStates::BUILD_CSR:            _state = handleBuildCSR          (); break;
+    case CSRHandlerStates::REQUEST_SIGNATURE:    _state = handleRequestSignature  (); break;
+    case CSRHandlerStates::WAITING_RESPONSE:     _state = handleWaitingResponse   (); break;
+    case CSRHandlerStates::PARSE_RESPONSE:       _state = handleParseResponse     (); break;
+    case CSRHandlerStates::BUILD_CERTIFICATE:    _state = handleBuildCertificate  (); break;
+    case CSRHandlerStates::CERT_CREATED:         _state = handleCertCreated       (); break;
+    case CSRHandlerStates::WAITING_COMPLETE_RES: _state = handleWaitingCompleteRes(); break;
+    case CSRHandlerStates::COMPLETED:                                                 break;
+    case CSRHandlerStates::ERROR:                         handleError             (); break;
+    case CSRHandlerStates::END:                                                       break;
+  }
+
+  return _state;
+}
+
+void CSRHandlerClass::updateNextRequestAt() {
+  uint32_t delay;
+  if(_requestAttempt <= MAX_CSR_REQUEST_INTERVAL_ATTEMPTS) {
+    delay = BACKEND_INTERVAL_s * _requestAttempt * 1000; // use linear backoff since backend has a rate limit
+  }else {
+    delay = MAX_CSR_REQUEST_INTERVAL;
+  }
+
+  _nextRequestAt = millis() + delay + jitter();
+}
+
+uint32_t CSRHandlerClass::jitter(uint32_t base, uint32_t max) {
+  srand(millis());
+  return base + rand() % (max - base);
+}
+
+bool CSRHandlerClass::postRequest(const char *url, String &postData) {
+  if(!_client){
+    _client = new HttpClient(*_tlsClient, server, 443);
+  }
+
+  uint32_t ts = getTimestamp();
+  if(ts == 0){
+    DEBUG_WARNING("CSRH::%s Failed getting timestamp", __FUNCTION__);
+    return false;
+  }
+
+  String token = getAIoTCloudJWT(*_secureElement, *_uhwid, ts, 1);
+
+  _requestAttempt++;
+  _client->beginRequest();
+
+  if(_client->post(url) == 0){
+    _client->sendHeader("Host", server);
+    _client->sendHeader("Connection", "close");
+    _client->sendHeader("Content-Type", "application/json;charset=UTF-8");
+    _client->sendHeader("Authorization", "Bearer " + token);
+    _client->sendHeader("Content-Length", postData.length());
+    _client->beginBody();
+    _client->print(postData);
+    _startWaitingResponse = millis();
+    return true;
+  }
+  return false;
+}
+
+uint32_t CSRHandlerClass::getTimestamp() {
+  uint8_t getTsAttempt = 0;
+  uint32_t ts = 0;
+  do{
+    TimeService.sync();
+    ts = TimeService.getTime();
+    getTsAttempt++;
+  }while(ts == 0 && getTsAttempt < 3);
+
+  return ts;
+}
+
+CSRHandlerClass::CSRHandlerStates CSRHandlerClass::handleBuildCSR() {
+  if (!_certForCSR) {
+    _certForCSR = new ECP256Certificate();
+  }
+  _certForCSR->begin();
+
+  _certForCSR->setSubjectCommonName(*_uhwid);
+
+  if (!SElementCSR::build(*_secureElement, *_certForCSR, static_cast<int>(SElementArduinoCloudSlot::Key), true)) {
+    DEBUG_ERROR("CSRH::%s Error generating CSR!", __FUNCTION__);
+    _ledFeedback.setMode(LEDFeedbackClass::LEDFeedbackMode::ERROR);
+    return CSRHandlerStates::ERROR;
+  }
+  return CSRHandlerStates::REQUEST_SIGNATURE;
+}
+
+CSRHandlerClass::CSRHandlerStates CSRHandlerClass::handleRequestSignature() {
+  CSRHandlerStates nextState = _state;
+
+  if(millis() < _nextRequestAt) {
+    return nextState;
+  }
+
+  NetworkConnectionState connectionRes = _connectionHandler->check();
+  if (connectionRes != NetworkConnectionState::CONNECTED) {
+    nextNetworkRetry();
+    return nextState;
+  }
+
+  if(!_certForCSR){
+    return CSRHandlerStates::BUILD_CSR;
+  }
+
+  String csr = _certForCSR->getCSRPEM();
+  csr.replace("\n", "\\n");
+
+  String PostData = "{\"csr\":\"";
+  PostData += csr;
+  PostData += "\"}";
+  DEBUG_INFO("CSRH Downloading certificate...");
+
+  if(postRequest("/provisioning/v1/onboarding/provision/csr", PostData)){
+    nextState = CSRHandlerStates::WAITING_RESPONSE;
+  } else {
+    updateNextRequestAt();
+    DEBUG_WARNING("CSRH::%s Failed sending request, retrying in %d ms", __FUNCTION__, _nextRequestAt - millis());
+  }
+
+  return nextState;
+}
+
+CSRHandlerClass::CSRHandlerStates CSRHandlerClass::handleWaitingResponse() {
+  CSRHandlerStates nextState = _state;
+  NetworkConnectionState connectionRes = _connectionHandler->check();
+  if (connectionRes != NetworkConnectionState::CONNECTED) {
+    nextNetworkRetry();
+    _client->stop();
+    return CSRHandlerStates::REQUEST_SIGNATURE;
+  }
+
+  if (millis() - _startWaitingResponse > RESPONSE_TIMEOUT) {
+    _client->stop();
+    updateNextRequestAt();
+    DEBUG_WARNING("CSRH::%s CSR request timeout, retrying in %d ms", __FUNCTION__, _nextRequestAt - millis());
+    nextState = CSRHandlerStates::REQUEST_SIGNATURE;
+  }
+
+
+  int statusCode = _client->responseStatusCode();
+  if(statusCode == 200){
+    nextState = CSRHandlerStates::PARSE_RESPONSE;
+  } else {
+    _client->stop();
+    updateNextRequestAt();
+    DEBUG_WARNING("CSRH::%s CSR request error code %d, retrying in %d ms", __FUNCTION__, statusCode ,_nextRequestAt - millis());
+    nextState = CSRHandlerStates::REQUEST_SIGNATURE;
+  }
+
+  return nextState;
+}
+
+CSRHandlerClass::CSRHandlerStates CSRHandlerClass::handleParseResponse() {
+  String certResponse = _client->responseBody();
+  _client->stop();
+
+  /* Parse the response in format:
+   * device_id|authority_key_identifier|not_before|serial|signature_asn1_x|signature_asn1_y
+   */
+  char *response = (char *)certResponse.c_str();
+  char *token[6];
+  int i = 1;
+  token[0] = strtok(response, "|");
+  for (; i < 6; i++) {
+    char *tok = strtok(NULL, "|");
+    if(tok == NULL){
+      break;
+    }
+    token[i] = tok;
+  }
+
+  if(i < 6 || strlen(token[0]) != 36 || strlen(token[1]) != 40
+   || strlen(token[2]) < 10 || strlen(token[3]) != 32
+   || strlen(token[4]) != 64 || strlen(token[5]) != 64
+   || sscanf(token[2], "%4d-%2d-%2dT%2d", &_issueYear, &_issueMonth, &_issueDay, &_issueHour) != 4){
+    updateNextRequestAt();
+    DEBUG_ERROR("CSRH::%s Error parsing response, retrying in %d ms", __FUNCTION__, _nextRequestAt - millis());
+    return CSRHandlerStates::REQUEST_SIGNATURE;
+  }
+
+  _deviceId = token[0];
+  hex::decode(token[1], _authorityKeyIdentifier, sizeof(_authorityKeyIdentifier));
+  hex::decode(token[3], _serialNumber, sizeof(_serialNumber));
+  hex::decode(token[4], _signature, sizeof(_signature));
+  hex::decode(token[5], &_signature[32], sizeof(_signature) - 32);
+
+  return CSRHandlerStates::BUILD_CERTIFICATE;
+}
+
+CSRHandlerClass::CSRHandlerStates CSRHandlerClass::handleBuildCertificate() {
+  int expireYears = 31;
+
+  if (!SElementArduinoCloudDeviceId::write(*_secureElement, _deviceId, SElementArduinoCloudSlot::DeviceId)) {
+    DEBUG_ERROR("CSRH::%s Error storing device id!", __FUNCTION__);
+    _ledFeedback.setMode(LEDFeedbackClass::LEDFeedbackMode::ERROR);
+    return CSRHandlerStates::ERROR;
+  }
+
+  ECP256Certificate cert;
+  cert.begin();
+
+  cert.setSubjectCommonName(_deviceId);
+  cert.setIssuerCountryName("US");
+  cert.setIssuerOrganizationName("Arduino LLC US");
+  cert.setIssuerOrganizationalUnitName("IT");
+  cert.setIssuerCommonName("Arduino");
+  cert.setSignature(_signature, sizeof(_signature));
+  cert.setAuthorityKeyId(_authorityKeyIdentifier, sizeof(_authorityKeyIdentifier));
+  cert.setSerialNumber(_serialNumber, sizeof(_serialNumber));
+  cert.setIssueYear(_issueYear);
+  cert.setIssueMonth(_issueMonth);
+  cert.setIssueDay(_issueDay);
+  cert.setIssueHour(_issueHour);
+  cert.setExpireYears(expireYears);
+
+  if (!SElementArduinoCloudCertificate::build(*_secureElement, cert, static_cast<int>(SElementArduinoCloudSlot::Key))) {
+    DEBUG_ERROR("CSRH::%s Error building secureElement compressed cert!", __FUNCTION__);
+    _ledFeedback.setMode(LEDFeedbackClass::LEDFeedbackMode::ERROR);
+    return CSRHandlerStates::ERROR;
+  }
+
+  if (!SElementArduinoCloudCertificate::write(*_secureElement, cert, SElementArduinoCloudSlot::CompressedCertificate)) {
+    DEBUG_ERROR("CSRH::%s Error storing cert!" , __FUNCTION__);
+    _ledFeedback.setMode(LEDFeedbackClass::LEDFeedbackMode::ERROR);
+    return CSRHandlerStates::ERROR;
+  }
+
+  DEBUG_INFO("CSRH Certificate created!");
+  _nextRequestAt = 0;
+  _requestAttempt = 0;
+  return CSRHandlerStates::CERT_CREATED;
+}
+
+CSRHandlerClass::CSRHandlerStates CSRHandlerClass::handleCertCreated() {
+  CSRHandlerStates nextState = _state;
+
+  if(millis() < _nextRequestAt) {
+    return nextState;
+  }
+
+  NetworkConnectionState connectionRes = _connectionHandler->check();
+  if (connectionRes != NetworkConnectionState::CONNECTED) {
+    nextNetworkRetry();
+    return nextState;
+  }
+
+  String PostData = "{\"wifi_fw_version\":\"";
+  PostData += _fw_version;
+  PostData += "\"}";
+  if(postRequest("/provisioning/v1/onboarding/provision/complete", PostData)){
+    nextState = CSRHandlerStates::WAITING_COMPLETE_RES;
+  } else {
+    updateNextRequestAt();
+    DEBUG_WARNING("CSRH::%s Error sending complete request, retrying in %d ms", __FUNCTION__, _nextRequestAt - millis());
+  }
+
+  return nextState;
+}
+
+CSRHandlerClass::CSRHandlerStates CSRHandlerClass::handleWaitingCompleteRes() {
+  CSRHandlerStates nextState = _state;
+  NetworkConnectionState connectionRes = _connectionHandler->check();
+  if (connectionRes != NetworkConnectionState::CONNECTED) {
+    nextNetworkRetry();
+    _client->stop();
+    return CSRHandlerStates::CERT_CREATED;
+  }
+
+  if (millis() - _startWaitingResponse > RESPONSE_TIMEOUT) {
+    _client->stop();
+    updateNextRequestAt();
+    DEBUG_WARNING("CSRH::%s Complete request timeout, retrying in %d ms", __FUNCTION__, _nextRequestAt - millis());
+    nextState = CSRHandlerStates::CERT_CREATED;
+  }
+
+  int statusCode = _client->responseStatusCode();
+  if(statusCode == 200){
+    if (_certForCSR) {
+      delete _certForCSR;
+      _certForCSR = nullptr;
+    }
+    DEBUG_INFO("CSRH Provisioning completed!");
+    nextState = CSRHandlerStates::COMPLETED;
+  } else if (statusCode == 429 || statusCode == 503) {
+    updateNextRequestAt();
+    nextState = CSRHandlerStates::CERT_CREATED;
+  } else {
+    DEBUG_WARNING("CSRH::%s Complete request error code %d, retrying in %d ms", __FUNCTION__, statusCode ,_nextRequestAt - millis());
+    _requestAttempt = 0;
+    _nextRequestAt = 0;
+    nextState = CSRHandlerStates::REQUEST_SIGNATURE;
+  }
+  _client->stop();
+
+  return nextState;
+}
+
+void CSRHandlerClass::nextNetworkRetry() {
+  _nextRequestAt = millis() + CONNECTION_RETRY_TIMEOUT_MS;
+}
+
+void CSRHandlerClass::handleError() {
+  _ledFeedback.update();
+}
diff --git a/examples/utility/Provisioning_2.0/CSRHandler.h b/examples/utility/Provisioning_2.0/CSRHandler.h
new file mode 100644
index 000000000..ae5956b7e
--- /dev/null
+++ b/examples/utility/Provisioning_2.0/CSRHandler.h
@@ -0,0 +1,74 @@
+/*
+  Copyright (c) 2024 Arduino SA
+
+  This Source Code Form is subject to the terms of the Mozilla Public
+  License, v. 2.0. If a copy of the MPL was not distributed with this
+  file, You can obtain one at http://mozilla.org/MPL/2.0/.
+*/
+
+#pragma once
+#include <Arduino.h>
+#include <Arduino_ConnectionHandler.h>
+#include <Arduino_SecureElement.h>
+#include <tls/utility/TLSClientMqtt.h>
+#include <ArduinoHttpClient.h>
+#include "utility/LEDFeedback.h"
+#define JITTER_BASE 0
+#define JITTER_MAX 1000
+
+class CSRHandlerClass {
+public:
+  CSRHandlerClass();
+  ~CSRHandlerClass();
+  enum class CSRHandlerStates {
+    BUILD_CSR,
+    REQUEST_SIGNATURE,
+    WAITING_RESPONSE,
+    PARSE_RESPONSE,
+    BUILD_CERTIFICATE,
+    CERT_CREATED,
+    WAITING_COMPLETE_RES,
+    COMPLETED,
+    ERROR,
+    END
+  };
+  bool begin(ConnectionHandler &connectionHandler, SecureElement &secureElement, String &uhwid);
+  void end();
+  CSRHandlerStates poll();
+private:
+  CSRHandlerStates _state;
+  unsigned long _nextRequestAt;
+  uint32_t _requestAttempt;
+  uint32_t _startWaitingResponse;
+  String *_uhwid;
+  String _fw_version;
+
+  int _issueYear;
+  uint8_t _issueMonth;
+  uint8_t _issueDay;
+  uint8_t _issueHour;
+  byte _serialNumber[16];
+  byte _authorityKeyIdentifier[20];
+  byte _signature[64];
+  String _deviceId;
+
+  ECP256Certificate *_certForCSR;
+  ConnectionHandler *_connectionHandler;
+  SecureElement *_secureElement;
+  TLSClientMqtt *_tlsClient;
+  HttpClient *_client;
+  LEDFeedbackClass &_ledFeedback;
+  void updateNextRequestAt();
+  void nextNetworkRetry();
+  uint32_t jitter(uint32_t base = JITTER_BASE, uint32_t max = JITTER_MAX);
+  bool postRequest(const char *url, String &postData);
+  uint32_t getTimestamp();
+  CSRHandlerStates handleBuildCSR();
+  CSRHandlerStates handleRequestSignature();
+  CSRHandlerStates handleWaitingResponse();
+  CSRHandlerStates handleParseResponse();
+  CSRHandlerStates handleBuildCertificate();
+  CSRHandlerStates handleCertCreated();
+  CSRHandlerStates handleWaitingCompleteRes();
+  void handleError();
+};
diff --git a/examples/utility/Provisioning_2.0/ClaimingHandler.cpp b/examples/utility/Provisioning_2.0/ClaimingHandler.cpp
new file mode 100644
index 000000000..7456f410b
--- /dev/null
+++ b/examples/utility/Provisioning_2.0/ClaimingHandler.cpp
@@ -0,0 +1,192 @@
+/*
+  Copyright (c) 2024 Arduino SA
+
+  This Source Code Form is subject to the terms of the Mozilla Public
+  License, v. 2.0. If a copy of the MPL was not distributed with this
+  file, You can obtain one at http://mozilla.org/MPL/2.0/.
+*/
+
+#include "ClaimingHandler.h"
+#include <utility/SElementArduinoCloudJWT.h>
+#include "Arduino_DebugUtils.h"
+#include <ArduinoBLE.h>
+#include "utility/HCI.h"
+#include <Arduino_HEX.h>
+
+extern const char *SKETCH_VERSION;
+
+ClaimingHandlerClass::ClaimingHandlerClass():
+  _uhwid {nullptr},
+  _state {ClaimingHandlerStates::END},
+  _secureElement {nullptr},
+  _clearStoredCredentials {nullptr},
+  _agentManager { AgentsManagerClass::getInstance()},
+  _ledFeedback {LEDFeedbackClass::getInstance()} {
+  _receivedEvent = ClaimingReqEvents::NONE;
+  _ts = 0;
+}
+
+bool ClaimingHandlerClass::begin(SecureElement &secureElement, String &uhwid, ClearStoredCredentialsHandler clearStoredCredentials) {
+  if(_state != ClaimingHandlerStates::END) {
+    return true;
+  }
+
+  if(uhwid == "" || clearStoredCredentials == nullptr) {
+    return false;
+  }
+
+  if (!_agentManager.addRequestHandler(RequestType::GET_ID, getIdRequestCb)) {
+    return false;
+  }
+
+  if (!_agentManager.addRequestHandler(RequestType::RESET, resetStoredCredRequestCb)) {
+    return false;
+  }
+
+  if(!_agentManager.addRequestHandler(RequestType::GET_BLE_MAC_ADDRESS, getBLEMacAddressRequestCb)) {
+    return false;
+  }
+
+  if(!_agentManager.addRequestHandler(RequestType::GET_PROVISIONING_SKETCH_VERSION, getProvSketchVersionRequestCb)) {
+    return false;
+  }
+
+  if (!_agentManager.addReturnTimestampCallback(setTimestamp)) {
+    return false;
+  }
+
+  _agentManager.begin();
+  _uhwid = &uhwid;
+  _secureElement = &secureElement;
+  _clearStoredCredentials = clearStoredCredentials;
+  _state = ClaimingHandlerStates::INIT;
+}
+
+void ClaimingHandlerClass::end() {
+  if(_state == ClaimingHandlerStates::END) {
+    return;
+  }
+
+  _agentManager.removeReturnTimestampCallback();
+  _agentManager.removeRequestHandler(RequestType::GET_ID);
+  _agentManager.removeRequestHandler(RequestType::RESET);
+  _agentManager.end();
+  _state = ClaimingHandlerStates::END;
+}
+
+void ClaimingHandlerClass::poll() {
+  if(_state == ClaimingHandlerStates::END) {
+    return;
+  }
+  _ledFeedback.update();
+  _agentManager.update();
+
+  switch (_receivedEvent) {
+    case ClaimingReqEvents::GET_ID:                  getIdReqHandler               (); break;
+    case ClaimingReqEvents::RESET:                   resetStoredCredReqHandler     (); break;
+    case ClaimingReqEvents::GET_BLE_MAC_ADDRESS:     getBLEMacAddressReqHandler    (); break;
+    case ClaimingReqEvents::GET_PROV_SKETCH_VERSION: getProvSketchVersionReqHandler(); break;
+  }
+  _receivedEvent = ClaimingReqEvents::NONE;
+  return;
+}
+
+void ClaimingHandlerClass::getIdReqHandler() {
+  if (_ts != 0) {
+    byte _uhwidBytes[32];
+    hex::decode(_uhwid->c_str(), _uhwidBytes, _uhwid->length());
+    //Send UHWID
+    ProvisioningOutputMessage idMsg = {MessageOutputType::UHWID};
+    idMsg.m.uhwid = _uhwidBytes;
+    _agentManager.sendMsg(idMsg);
+
+    String token = getAIoTCloudJWT(*_secureElement, *_uhwid, _ts, 1);
+    if (token == "") {
+      DEBUG_ERROR("CH::%s Error: token not created", __FUNCTION__);
+      sendStatus(StatusMessage::ERROR);
+      return;
+    }
+
+    //Send JWT
+    ProvisioningOutputMessage jwtMsg = {MessageOutputType::JWT};
+    jwtMsg.m.jwt = token.c_str();
+    _agentManager.sendMsg(jwtMsg);
+    _ts = 0;
+  } else {
+    DEBUG_ERROR("CH::%s Error: timestamp not provided" , __FUNCTION__);
+    sendStatus(StatusMessage::PARAMS_NOT_FOUND);
+  }
+}
+
+void ClaimingHandlerClass::resetStoredCredReqHandler() {
+  if( !_clearStoredCredentials()){
+    DEBUG_ERROR("CH::%s Error: reset stored credentials failed", __FUNCTION__);
+    sendStatus(StatusMessage::ERROR);
+  } else {
+    sendStatus(StatusMessage::RESET_COMPLETED);
+  }
+
+}
+
+void ClaimingHandlerClass::getBLEMacAddressReqHandler() {
+  uint8_t mac[6] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+  bool activated = false;
+  ConfiguratorAgent * connectedAgent = _agentManager.getConnectedAgent();
+  if(!_agentManager.isAgentEnabled(ConfiguratorAgent::AgentTypes::BLE) || (connectedAgent != nullptr &&
+    connectedAgent->getAgentType() != ConfiguratorAgent::AgentTypes::BLE)) {
+      activated = true;
+      BLE.begin();
+  }
+
+  HCI.readBdAddr(mac);
+
+  for(int i = 0; i < 3; i++){
+    uint8_t byte = mac[i];
+    mac[i] = mac[5-i];
+    mac[5-i] = byte;
+  }
+  if (activated) {
+    BLE.end();
+  }
+
+  ProvisioningOutputMessage outputMsg;
+  outputMsg.type = MessageOutputType::BLE_MAC_ADDRESS;
+  outputMsg.m.BLEMacAddress = mac;
+  _agentManager.sendMsg(outputMsg);
+}
+
+void ClaimingHandlerClass::getProvSketchVersionReqHandler() {
+  ProvisioningOutputMessage outputMsg;
+  outputMsg.type = MessageOutputType::PROV_SKETCH_VERSION;
+  outputMsg.m.provSketchVersion = SKETCH_VERSION;
+  _agentManager.sendMsg(outputMsg);
+}
+
+void ClaimingHandlerClass::getIdRequestCb() {
+  DEBUG_VERBOSE("CH Get ID request received");
+  _receivedEvent = ClaimingReqEvents::GET_ID;
+}
+void ClaimingHandlerClass::setTimestamp(uint64_t ts) {
+  _ts = ts;
+}
+
+void ClaimingHandlerClass::resetStoredCredRequestCb() {
+  DEBUG_VERBOSE("CH Reset stored credentials request received");
+  _receivedEvent = ClaimingReqEvents::RESET;
+}
+
+void ClaimingHandlerClass::getBLEMacAddressRequestCb() {
+  DEBUG_VERBOSE("CH Get BLE MAC address request received");
+  _receivedEvent = ClaimingReqEvents::GET_BLE_MAC_ADDRESS;
+}
+
+void ClaimingHandlerClass::getProvSketchVersionRequestCb() {
+  DEBUG_VERBOSE("CH Get provisioning sketch version request received");
+  _receivedEvent = ClaimingReqEvents::GET_PROV_SKETCH_VERSION;
+}
+
+bool ClaimingHandlerClass::sendStatus(StatusMessage msg) {
+  ProvisioningOutputMessage statusMsg = { MessageOutputType::STATUS, { msg } };
+  return _agentManager.sendMsg(statusMsg);
+}
diff --git a/examples/utility/Provisioning_2.0/ClaimingHandler.h b/examples/utility/Provisioning_2.0/ClaimingHandler.h
new file mode 100644
index 000000000..77f2ebea6
--- /dev/null
+++ b/examples/utility/Provisioning_2.0/ClaimingHandler.h
@@ -0,0 +1,53 @@
+/*
+  Copyright (c) 2024 Arduino SA
+
+  This Source Code Form is subject to the terms of the Mozilla Public
+  License, v. 2.0. If a copy of the MPL was not distributed with this
+  file, You can obtain one at http://mozilla.org/MPL/2.0/.
+*/
+
+#pragma once
+#include "Arduino.h"
+#include "configuratorAgents/AgentsManager.h"
+#include <Arduino_SecureElement.h>
+#include "utility/LEDFeedback.h"
+
+typedef bool (*ClearStoredCredentialsHandler)();
+class ClaimingHandlerClass {
+public:
+  ClaimingHandlerClass();
+  bool begin(SecureElement &secureElement, String &uhwid, ClearStoredCredentialsHandler clearStoredCredentials);
+  void end();
+  void poll();
+private:
+  String *_uhwid;
+  enum class ClaimingHandlerStates {
+    INIT,
+    END
+  };
+  enum class ClaimingReqEvents { NONE,
+                                 GET_ID,
+                                 RESET,
+                                 GET_BLE_MAC_ADDRESS,
+                                 GET_PROV_SKETCH_VERSION};
+  static inline ClaimingReqEvents _receivedEvent;
+  ClaimingHandlerStates _state;
+  AgentsManagerClass &_agentManager;
+  LEDFeedbackClass &_ledFeedback;
+  static inline uint64_t _ts;
+  SecureElement *_secureElement;
+
+  bool sendStatus(StatusMessage msg);
+  /* Commands handlers */
+  void getIdReqHandler();
+  void resetStoredCredReqHandler();
+  void getBLEMacAddressReqHandler();
+  void getProvSketchVersionReqHandler();
+  ClearStoredCredentialsHandler _clearStoredCredentials;
+  /* Callbacks for receiving commands */
+  static void getIdRequestCb();
+  static void setTimestamp(uint64_t ts);
+  static void resetStoredCredRequestCb();
+  static void getBLEMacAddressRequestCb();
+  static void getProvSketchVersionRequestCb();
+};
diff --git a/examples/utility/Provisioning_2.0/Provisioning_2.0.ino b/examples/utility/Provisioning_2.0/Provisioning_2.0.ino
new file mode 100644
index 000000000..3ab02a278
--- /dev/null
+++ b/examples/utility/Provisioning_2.0/Provisioning_2.0.ino
@@ -0,0 +1,228 @@
+/*
+  Copyright (c) 2024 Arduino SA
+
+  This Source Code Form is subject to the terms of the Mozilla Public
+  License, v. 2.0. If a copy of the MPL was not distributed with this
+  file, You can obtain one at http://mozilla.org/MPL/2.0/.
+*/
+#include "thingProperties.h"
+#include "CSRHandler.h"
+#include "ClaimingHandler.h"
+#include "SecretsHelper.h"
+#include <Arduino_FlashFormatter.h>
+#include <Arduino_SecureElement.h>
+#include <utility/SElementArduinoCloudDeviceId.h>
+#include <utility/SElementArduinoCloudCertificate.h>
+#include "utility/LEDFeedback.h"
+
+const char *SKETCH_VERSION = "0.1.0";
+
+enum class DeviceState {
+    HARDWARE_CHECK,
+    BEGIN,
+    NETWORK_CONFIG,
+    CSR,
+    BEGIN_CLOUD,
+    RUN,
+    ERROR
+  };
+
+DeviceState _state = DeviceState::HARDWARE_CHECK;
+SecureElement secureElement;
+
+String uhwid = "";
+bool resetEvent = false;
+
+CSRHandlerClass CSRHandler;
+ClaimingHandlerClass ClaimingHandler;
+
+bool clearStoredCredentials() {
+  const uint8_t empty[4] = {0x00,0x00,0x00,0x00};
+  if(!NetworkConfigurator.resetStoredConfiguration() || \
+    !secureElement.writeSlot(static_cast<int>(SElementArduinoCloudSlot::DeviceId), (byte*)empty, sizeof(empty)) || \
+    !secureElement.writeSlot(static_cast<int>(SElementArduinoCloudSlot::CompressedCertificate), (byte*)empty, sizeof(empty))) {
+    return false;
+  }
+
+  ArduinoCloud.disconnect();
+  resetEvent = true;
+  return true;
+  }
+
+void setup() {
+  Serial.begin(9600);
+
+  delay(1500);
+
+  setDebugMessageLevel(4);
+
+  initProperties();
+  AgentsManagerClass::getInstance().begin();
+  LEDFeedbackClass::getInstance().begin();
+  DEBUG_INFO("Starting Provisioning");
+}
+
+void sendStatus(StatusMessage msg) {
+  ProvisioningOutputMessage outMsg = { MessageOutputType::STATUS, { msg } };
+  AgentsManagerClass::getInstance().sendMsg(outMsg);
+}
+
+DeviceState handleHardwareCheck() {
+  // Init the secure element
+  if(!secureElement.begin()) {
+    DEBUG_ERROR("Sketch: Error during secureElement begin!");
+    LEDFeedbackClass::getInstance().setMode(LEDFeedbackClass::LEDFeedbackMode::ERROR);
+    sendStatus(StatusMessage::HW_ERROR_SE_BEGIN);
+    return DeviceState::ERROR;
+  }
+
+  if (!secureElement.locked()) {
+    if (!secureElement.writeConfiguration()) {
+      DEBUG_ERROR("Sketch: Writing secureElement configuration failed!");
+      LEDFeedbackClass::getInstance().setMode(LEDFeedbackClass::LEDFeedbackMode::ERROR);
+      sendStatus(StatusMessage::HW_ERROR_SE_CONFIG);
+      return DeviceState::ERROR;
+    }
+
+    if (!secureElement.lock()) {
+      DEBUG_ERROR("Sketch: Locking secureElement configuration failed!");
+      LEDFeedbackClass::getInstance().setMode(LEDFeedbackClass::LEDFeedbackMode::ERROR);
+      sendStatus(StatusMessage::HW_ERROR_SE_LOCK);
+      return DeviceState::ERROR;
+    }
+    DEBUG_INFO("secureElement locked successfully");
+  }
+
+  FlashFormatter flashFormatter;
+  // Check if the board storage is properly formatted
+  if(!flashFormatter.checkAndFormatPartition()) {
+    DEBUG_ERROR("Sketch: Error partitioning storage");
+    LEDFeedbackClass::getInstance().setMode(LEDFeedbackClass::LEDFeedbackMode::ERROR);
+    sendStatus(StatusMessage::FAIL_TO_PARTITION_STORAGE);
+    return DeviceState::ERROR;
+  }
+
+  return DeviceState::BEGIN;
+}
+
+DeviceState handleBegin() {
+  uhwid = GetUHWID();
+  if(uhwid == ""){
+    DEBUG_ERROR("Sketch: Error getting UHWID");
+    LEDFeedbackClass::getInstance().setMode(LEDFeedbackClass::LEDFeedbackMode::ERROR);
+    sendStatus(StatusMessage::ERROR_GENERATING_UHWID);
+    return DeviceState::ERROR;
+  }
+  // Scan the network options
+  NetworkConfigurator.scanNetworkOptions();
+  NetworkConfigurator.begin();
+  ClaimingHandler.begin(secureElement, uhwid, clearStoredCredentials);
+  DEBUG_INFO("BLE Available");
+  return DeviceState::NETWORK_CONFIG;
+}
+
+DeviceState handleNetworkConfig() {
+  ClaimingHandler.poll();
+  if(resetEvent){
+    resetEvent = false;
+  }
+  NetworkConfiguratorStates s = NetworkConfigurator.update();
+
+  DeviceState nextState = _state;
+  if (s == NetworkConfiguratorStates::CONFIGURED) {
+    String deviceId = "";
+    SElementArduinoCloudDeviceId::read(secureElement, deviceId, SElementArduinoCloudSlot::DeviceId);
+
+    if (deviceId == "") {
+      CSRHandler.begin(ArduinoIoTPreferredConnection, secureElement, uhwid);
+      nextState = DeviceState::CSR;
+    } else {
+      nextState = DeviceState::BEGIN_CLOUD;
+    }
+  }
+  return nextState;
+}
+
+DeviceState handleCSR() {
+  NetworkConfigurator.update();
+  ClaimingHandler.poll();
+  if(resetEvent) {
+    resetEvent = false;
+    CSRHandler.end();
+    return DeviceState::NETWORK_CONFIG;
+  }
+
+  DeviceState nextState = _state;
+
+  CSRHandlerClass::CSRHandlerStates res = CSRHandler.poll();
+  if (res == CSRHandlerClass::CSRHandlerStates::COMPLETED) {
+    CSRHandler.end();
+    nextState = DeviceState::BEGIN_CLOUD;
+  }
+
+  return nextState;
+}
+
+DeviceState handleBeginCloud() {
+  // Close the connection to the peer (App mobile, FE, etc)
+  NetworkConfigurator.disconnectAgent();
+  // Close the BLE connectivity
+  if (NetworkConfigurator.isAgentEnabled(ConfiguratorAgent::AgentTypes::BLE)) {
+    NetworkConfigurator.enableAgent(ConfiguratorAgent::AgentTypes::BLE, false);
+  }
+  // Connect to Arduino IoT Cloud
+#ifdef COMPILE_TEST
+  ArduinoCloud.begin(ArduinoIoTPreferredConnection, false, "mqtts-sa.iot.oniudra.cc");
+#else
+  ArduinoCloud.begin(ArduinoIoTPreferredConnection);
+#endif
+  ArduinoCloud.printDebugInfo();
+
+  return DeviceState::RUN;
+}
+
+void cloudConnectedHandler(bool connected) {
+  static bool _status = false;
+  if(connected != _status){
+    _status = connected;
+    if(connected){
+      LEDFeedbackClass::getInstance().setMode(LEDFeedbackClass::LEDFeedbackMode::CONNECTED_TO_CLOUD);
+    } else {
+      LEDFeedbackClass::getInstance().setMode(LEDFeedbackClass::LEDFeedbackMode::NONE);
+    }
+  }
+}
+
+DeviceState handleRun() {
+  ClaimingHandler.poll();
+  if(resetEvent) {
+    resetEvent = false;
+    return DeviceState::NETWORK_CONFIG;
+  }
+
+  DeviceState nextState = _state;
+  ArduinoCloud.update();
+
+  cloudConnectedHandler(ArduinoCloud.connected());
+
+  return nextState;
+}
+
+DeviceState handleError() {
+  LEDFeedbackClass::getInstance().update();
+  AgentsManagerClass::getInstance().update();
+  return DeviceState::ERROR;
+}
+
+void loop() {
+  switch (_state) {
+    case DeviceState::HARDWARE_CHECK:  _state = handleHardwareCheck(); break;
+    case DeviceState::BEGIN:           _state = handleBegin        (); break;
+    case DeviceState::NETWORK_CONFIG : _state = handleNetworkConfig(); break;
+    case DeviceState::CSR:             _state = handleCSR          (); break;
+    case DeviceState::BEGIN_CLOUD:     _state = handleBeginCloud   (); break;
+    case DeviceState::RUN:             _state = handleRun          (); break;
+    case DeviceState::ERROR:           _state = handleError        (); break;
+    default:                                                           break;
+  }
+}
diff --git a/examples/utility/Provisioning_2.0/SecretsHelper.h b/examples/utility/Provisioning_2.0/SecretsHelper.h
new file mode 100644
index 000000000..bdb6354d1
--- /dev/null
+++ b/examples/utility/Provisioning_2.0/SecretsHelper.h
@@ -0,0 +1,20 @@
+/*
+  Copyright (c) 2024 Arduino SA
+
+  This Source Code Form is subject to the terms of the Mozilla Public
+  License, v. 2.0. If a copy of the MPL was not distributed with this
+  file, You can obtain one at http://mozilla.org/MPL/2.0/.
+*/
+
+#pragma once
+
+#include <Arduino.h>
+#include <Arduino_UniqueHWId.h>
+
+inline String GetUHWID() {
+  UniqueHWId Id;
+  if (Id.begin()) {
+    return Id.get();
+  }
+  return "";
+}
diff --git a/examples/utility/Provisioning_2.0/thingProperties.h b/examples/utility/Provisioning_2.0/thingProperties.h
new file mode 100644
index 000000000..7f51fa6ca
--- /dev/null
+++ b/examples/utility/Provisioning_2.0/thingProperties.h
@@ -0,0 +1,32 @@
+/*
+  Copyright (c) 2024 Arduino SA
+
+  This Source Code Form is subject to the terms of the Mozilla Public
+  License, v. 2.0. If a copy of the MPL was not distributed with this
+  file, You can obtain one at http://mozilla.org/MPL/2.0/.
+*/
+#if defined(ARDUINO_SAMD_MKRGSM1400) || defined(ARDUINO_SAMD_MKRNB1500) || defined(ARDUINO_SAMD_MKRWAN1300) || defined(ARDUINO_SAMD_MKRWAN1310)
+#error "Board not supported for Provisioning 2.0"
+#endif
+
+#include <ArduinoIoTCloud.h>
+#include <GenericConnectionHandler.h>
+#include <Arduino_KVStore.h>
+#include "configuratorAgents/agents/BLEAgent.h"
+#include "configuratorAgents/agents/SerialAgent.h"
+
+GenericConnectionHandler ArduinoIoTPreferredConnection;
+KVStore kvStore;
+BLEAgentClass BLEAgent;
+SerialAgentClass SerialAgent;
+NetworkConfiguratorClass NetworkConfigurator(ArduinoIoTPreferredConnection);
+
+void initProperties() {
+
+  NetworkConfigurator.addAgent(BLEAgent);
+  NetworkConfigurator.addAgent(SerialAgent);
+  NetworkConfigurator.setStorage(kvStore);
+  ArduinoCloud.setConfigurator(NetworkConfigurator);
+}
+
+
diff --git a/library.properties b/library.properties
index 22e037e17..b2dbaea2a 100644
--- a/library.properties
+++ b/library.properties
@@ -8,4 +8,4 @@ category=Communication
 url=https://github.com/arduino-libraries/ArduinoIoTCloud
 architectures=mbed,samd,esp8266,mbed_nano,mbed_portenta,mbed_nicla,esp32,mbed_opta,mbed_giga,renesas_portenta,renesas_uno,mbed_edge,stm32
 includes=ArduinoIoTCloud.h
-depends=Arduino_ConnectionHandler,Arduino_DebugUtils,Arduino_SecureElement,ArduinoMqttClient,ArduinoECCX08,RTCZero,Adafruit SleepyDog Library,ArduinoHttpClient,Arduino_CloudUtils,ArduinoBearSSL
+depends=Arduino_ConnectionHandler,Arduino_DebugUtils,Arduino_SecureElement,ArduinoMqttClient,ArduinoECCX08,RTCZero,Adafruit SleepyDog Library,ArduinoHttpClient,Arduino_CloudUtils,ArduinoBearSSL,Arduino_NetworkConfigurator
diff --git a/src/AIoTC_Config.h b/src/AIoTC_Config.h
index 89f04e20f..c02d20b78 100644
--- a/src/AIoTC_Config.h
+++ b/src/AIoTC_Config.h
@@ -148,6 +148,13 @@
   #define BOARD_STM32H7
 #endif
 
+#if defined(ARDUINO_PORTENTA_H7_M7) || defined(ARDUINO_NICLA_VISION) || defined(ARDUINO_OPTA) || defined(ARDUINO_GIGA) \
+  || defined(ARDUINO_UNOR4_WIFI) || defined(ARDUINO_PORTENTA_C33)
+  #define NETWORK_CONFIGURATOR_ENABLED (1)
+#else
+  #define NETWORK_CONFIGURATOR_ENABLED (0)
+#endif
+
 /******************************************************************************
  * CONSTANTS
  ******************************************************************************/
diff --git a/src/ArduinoIoTCloud.cpp b/src/ArduinoIoTCloud.cpp
index a565cac44..74443ff5b 100644
--- a/src/ArduinoIoTCloud.cpp
+++ b/src/ArduinoIoTCloud.cpp
@@ -27,6 +27,9 @@
 
 ArduinoIoTCloudClass::ArduinoIoTCloudClass()
 : _connection{nullptr}
+#if NETWORK_CONFIGURATOR_ENABLED
+, _configurator{nullptr}
+#endif
 , _time_service(TimeService)
 , _thing_id{"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"}
 , _lib_version{AIOT_CONFIG_LIB_VERSION}
diff --git a/src/ArduinoIoTCloud.h b/src/ArduinoIoTCloud.h
index 7cea9382e..4c12a6f7b 100644
--- a/src/ArduinoIoTCloud.h
+++ b/src/ArduinoIoTCloud.h
@@ -25,6 +25,9 @@
 #include <AIoTC_Config.h>
 
 #include <Arduino_ConnectionHandler.h>
+#if NETWORK_CONFIGURATOR_ENABLED
+#include <Arduino_NetworkConfigurator.h>
+#endif
 
 #if defined(DEBUG_ERROR) || defined(DEBUG_WARNING) || defined(DEBUG_INFO) || defined(DEBUG_DEBUG) || defined(DEBUG_VERBOSE)
 #  include <Arduino_DebugUtils.h>
@@ -87,7 +90,7 @@ class ArduinoIoTCloudClass
     virtual void update        () = 0;
     virtual int  connected     () = 0;
     virtual void printDebugInfo() = 0;
-
+    virtual void disconnect    () { }
             void push();
             bool setTimestamp(String const & prop_name, unsigned long const timestamp);
 
@@ -101,6 +104,9 @@ class ArduinoIoTCloudClass
     inline unsigned long getInternalTime()              { return _time_service.getTime(); }
     inline unsigned long getLocalTime()                 { return _time_service.getLocalTime(); }
 
+    #if NETWORK_CONFIGURATOR_ENABLED
+    inline void setConfigurator(NetworkConfiguratorClass & configurator) { _configurator = &configurator; }
+    #endif
     void addCallback(ArduinoIoTCloudEvent const event, OnCloudEventCallback callback);
 
 #define addProperty( v, ...) addPropertyReal(v, #v, __VA_ARGS__)
@@ -146,6 +152,9 @@ class ArduinoIoTCloudClass
   protected:
 
     ConnectionHandler * _connection;
+    #if NETWORK_CONFIGURATOR_ENABLED
+    NetworkConfiguratorClass * _configurator;
+    #endif
     TimeServiceClass & _time_service;
     String _thing_id;
     String _lib_version;
diff --git a/src/ArduinoIoTCloudTCP.cpp b/src/ArduinoIoTCloudTCP.cpp
index dc8bb86d0..8cb6daba1 100644
--- a/src/ArduinoIoTCloudTCP.cpp
+++ b/src/ArduinoIoTCloudTCP.cpp
@@ -35,7 +35,7 @@
 #include <typeinfo>
 
 /******************************************************************************
-   LOCAL MODULE FUNCTIONS
+  LOCAL MODULE FUNCTIONS
  ******************************************************************************/
 
 unsigned long getTime()
@@ -48,7 +48,7 @@ unsigned long getTime()
  ******************************************************************************/
 
 ArduinoIoTCloudTCP::ArduinoIoTCloudTCP()
-: _state{State::ConnectPhy}
+: _state{State::ConfigPhy}
 , _connection_attempt(0,0)
 , _message_stream(std::bind(&ArduinoIoTCloudTCP::sendMessage, this, std::placeholders::_1))
 , _thing(&_message_stream)
@@ -79,29 +79,21 @@ ArduinoIoTCloudTCP::ArduinoIoTCloudTCP()
  * PUBLIC MEMBER FUNCTIONS
  ******************************************************************************/
 
-int ArduinoIoTCloudTCP::begin(ConnectionHandler & connection, bool const enable_watchdog, String brokerAddress, uint16_t brokerPort)
+int ArduinoIoTCloudTCP::begin(ConnectionHandler & connection, bool const enable_watchdog, String brokerAddress, uint16_t brokerPort, bool auto_reconnect)
 {
   _connection = &connection;
   _brokerAddress = brokerAddress;
 
-  ArduinoIoTAuthenticationMode authMode = ArduinoIoTAuthenticationMode::CERTIFICATE;
+  _authMode = ArduinoIoTAuthenticationMode::CERTIFICATE;
 #if defined (BOARD_HAS_SECRET_KEY)
   /* If board supports and sketch is configured for username and password login */
   if(_password.length()) {
-    authMode = ArduinoIoTAuthenticationMode::PASSWORD;
+    _authMode = ArduinoIoTAuthenticationMode::PASSWORD;
   }
 #endif
 
-  /* Setup broker TLS client */
-  _brokerClient.begin(connection, authMode);
-
-#if  OTA_ENABLED
-  /* Setup OTA TLS client */
-  _otaClient.begin(connection);
-#endif
-
   /* If board is configured for certificate authentication and mTLS */
-  if(authMode == ArduinoIoTAuthenticationMode::CERTIFICATE)
+  if(_authMode == ArduinoIoTAuthenticationMode::CERTIFICATE)
   {
 #if defined(BOARD_HAS_SECURE_ELEMENT)
     if (!_selement.begin())
@@ -141,18 +133,19 @@ int ArduinoIoTCloudTCP::begin(ConnectionHandler & connection, bool const enable_
     _brokerPort = (brokerPort == DEFAULT_BROKER_PORT_AUTO) ? DEFAULT_BROKER_PORT_USER_PASS_AUTH : brokerPort;
   }
 
-  /* Setup TimeService */
-  _time_service.begin(_connection);
-
   /* Setup retry timers */
   _connection_attempt.begin(AIOT_CONFIG_RECONNECTION_RETRY_DELAY_ms, AIOT_CONFIG_MAX_RECONNECTION_RETRY_DELAY_ms);
-  return begin(enable_watchdog, _brokerAddress, _brokerPort);
+  return begin(enable_watchdog, _brokerAddress, _brokerPort, auto_reconnect);
 }
 
-int ArduinoIoTCloudTCP::begin(bool const enable_watchdog, String brokerAddress, uint16_t brokerPort)
+int ArduinoIoTCloudTCP::begin(bool const enable_watchdog, String brokerAddress, uint16_t brokerPort, bool auto_reconnect)
 {
+  _enable_watchdog = enable_watchdog;
   _brokerAddress = brokerAddress;
   _brokerPort = brokerPort;
+  _auto_reconnect = auto_reconnect;
+
+  _state = State::ConfigPhy;
 
   _mqttClient.setClient(_brokerClient);
 
@@ -195,20 +188,12 @@ int ArduinoIoTCloudTCP::begin(bool const enable_watchdog, String brokerAddress,
   }
 #endif
 
-  /* Since we do not control what code the user inserts
-   * between ArduinoIoTCloudTCP::begin() and the first
-   * call to ArduinoIoTCloudTCP::update() it is wise to
-   * set a rather large timeout at first.
-   */
-#if defined (ARDUINO_ARCH_SAMD) || defined (ARDUINO_ARCH_MBED)
-  if (enable_watchdog) {
-    /* Initialize watchdog hardware */
-    watchdog_enable();
-    /* Setup callbacks to feed the watchdog during offloaded network operations (connection/download)*/
-    watchdog_enable_network_feed(_connection->getInterface());
+#if NETWORK_CONFIGURATOR_ENABLED
+  if(_configurator != nullptr){
+    _configurator->enableAgent(ConfiguratorAgent::AgentTypes::BLE,false);
+    _configurator->begin();
   }
 #endif
-
   return 1;
 }
 
@@ -225,12 +210,16 @@ void ArduinoIoTCloudTCP::update()
   State next_state = _state;
   switch (_state)
   {
+  case State::ConfigPhy:            next_state = handle_ConfigPhy();            break;
+  case State::Init:                 next_state = handle_Init();                 break;
   case State::ConnectPhy:           next_state = handle_ConnectPhy();           break;
   case State::SyncTime:             next_state = handle_SyncTime();             break;
   case State::ConnectMqttBroker:    next_state = handle_ConnectMqttBroker();    break;
   case State::Connected:            next_state = handle_Connected();            break;
   case State::Disconnect:           next_state = handle_Disconnect();           break;
+  case State::Disconnected:                                                     break;
   }
+
   _state = next_state;
 
   /* This watchdog feed is actually needed only by the RP2040 Connect because its
@@ -241,11 +230,24 @@ void ArduinoIoTCloudTCP::update()
   watchdog_reset();
 #endif
 
+  /* Poll the network configurator to check if it is updating the configuration.
+   * The polling must be performed only if the the first configuration is completed.
+   */
+  #if NETWORK_CONFIGURATOR_ENABLED
+  if(_configurator != nullptr && _state > State::Init && _configurator->update() == NetworkConfiguratorStates::UPDATING_CONFIG){
+    _state = State::ConfigPhy;
+  }
+  #endif
+
 #if OTA_ENABLED
   /* OTA FSM needs to reach the Idle state before being able to run independently from
    * the mqttClient. The state can be reached only after the mqttClient is connected to
    * the broker.
    */
+  if(_state <= State::Init){
+    return;
+  }
+
   if((_ota.getState() != OTACloudProcessInterface::Resume &&
       _ota.getState() != OTACloudProcessInterface::OtaBegin) ||
       _mqttClient.connected()) {
@@ -262,6 +264,9 @@ void ArduinoIoTCloudTCP::update()
 
 int ArduinoIoTCloudTCP::connected()
 {
+  if (_state <= State::Init) {
+    return 0;
+  }
   return _mqttClient.connected();
 }
 
@@ -272,10 +277,69 @@ void ArduinoIoTCloudTCP::printDebugInfo()
   DEBUG_INFO("MQTT Broker: %s:%d", _brokerAddress.c_str(), _brokerPort);
 }
 
+void ArduinoIoTCloudTCP::disconnect() {
+  if (_state == State::ConfigPhy || _state == State::Init) {
+    return;
+  }
+
+  _mqttClient.stop();
+  _auto_reconnect = false;
+  _state = State::Disconnect;
+}
+
 /******************************************************************************
  * PRIVATE MEMBER FUNCTIONS
  ******************************************************************************/
 
+ArduinoIoTCloudTCP::State ArduinoIoTCloudTCP::handle_ConfigPhy()
+{
+#if NETWORK_CONFIGURATOR_ENABLED
+  if (_configurator == nullptr) {
+    return State::Init;
+  }
+
+  if(_configurator->update() == NetworkConfiguratorStates::CONFIGURED) {
+      _configurator->disconnectAgent();
+      return State::Init;
+    }
+  return State::ConfigPhy;
+#else
+  return State::Init;
+#endif
+
+}
+
+ArduinoIoTCloudTCP::State ArduinoIoTCloudTCP::handle_Init()
+{
+  /* Setup broker TLS client */
+  /* Setup broker TLS client */
+  _brokerClient.begin(*_connection, _authMode);
+
+#if  OTA_ENABLED
+  /* Setup OTA TLS client */
+  _otaClient.begin(*_connection);
+#endif
+
+  /* Setup TimeService */
+  _time_service.begin(_connection);
+
+  /* Since we do not control what code the user inserts
+   * between ArduinoIoTCloudTCP::begin() and the first
+   * call to ArduinoIoTCloudTCP::update() it is wise to
+   * set a rather large timeout at first.
+   */
+#if defined (ARDUINO_ARCH_SAMD) || defined (ARDUINO_ARCH_MBED)
+  if (_enable_watchdog) {
+    /* Initialize watchdog hardware */
+    watchdog_enable();
+    /* Setup callbacks to feed the watchdog during offloaded network operations (connection/download)*/
+    watchdog_enable_network_feed(_connection->getInterface());
+  }
+#endif
+
+  return State::ConnectPhy;
+}
+
 ArduinoIoTCloudTCP::State ArduinoIoTCloudTCP::handle_ConnectPhy()
 {
   if (_connection->check() == NetworkConnectionState::CONNECTED)
@@ -381,9 +445,13 @@ ArduinoIoTCloudTCP::State ArduinoIoTCloudTCP::handle_Disconnect()
   DEBUG_INFO("Disconnected from Arduino IoT Cloud");
   execCloudEventCallback(ArduinoIoTCloudEvent::DISCONNECT);
 
-  /* Setup timer for broker connection and restart */
-  _connection_attempt.begin(AIOT_CONFIG_RECONNECTION_RETRY_DELAY_ms, AIOT_CONFIG_MAX_RECONNECTION_RETRY_DELAY_ms);
-  return State::ConnectPhy;
+  if(_auto_reconnect) {
+    /* Setup timer for broker connection and restart */
+    _connection_attempt.begin(AIOT_CONFIG_RECONNECTION_RETRY_DELAY_ms, AIOT_CONFIG_MAX_RECONNECTION_RETRY_DELAY_ms);
+    return State::ConnectPhy;
+  }
+
+  return State::Disconnected;
 }
 
 void ArduinoIoTCloudTCP::onMessage(int length)
@@ -630,11 +698,12 @@ int ArduinoIoTCloudTCP::updateCertificate(String authorityKeyIdentifier, String
   }
   return 0;
 }
+
 #endif
 
 /******************************************************************************
- * EXTERN DEFINITION
- ******************************************************************************/
+* EXTERN DEFINITION
+******************************************************************************/
 
 ArduinoIoTCloudTCP ArduinoCloud;
 
diff --git a/src/ArduinoIoTCloudTCP.h b/src/ArduinoIoTCloudTCP.h
index 6560a5f70..d59c6de97 100644
--- a/src/ArduinoIoTCloudTCP.h
+++ b/src/ArduinoIoTCloudTCP.h
@@ -72,9 +72,10 @@ class ArduinoIoTCloudTCP: public ArduinoIoTCloudClass
     virtual void update        () override;
     virtual int  connected     () override;
     virtual void printDebugInfo() override;
+    virtual void disconnect    () override;
 
-    int begin(ConnectionHandler & connection, bool const enable_watchdog = true, String brokerAddress = DEFAULT_BROKER_ADDRESS, uint16_t brokerPort = DEFAULT_BROKER_PORT_AUTO);
-    int begin(bool const enable_watchdog = true, String brokerAddress = DEFAULT_BROKER_ADDRESS, uint16_t brokerPort = DEFAULT_BROKER_PORT_AUTO);
+    int begin(ConnectionHandler & connection, bool const enable_watchdog = true, String brokerAddress = DEFAULT_BROKER_ADDRESS, uint16_t brokerPort = DEFAULT_BROKER_PORT_AUTO, bool auto_reconnect = true);
+    int begin(bool const enable_watchdog = true, String brokerAddress = DEFAULT_BROKER_ADDRESS, uint16_t brokerPort = DEFAULT_BROKER_PORT_AUTO, bool auto_reconnect = true);
 
 #if defined(BOARD_HAS_SECURE_ELEMENT)
     int updateCertificate(String authorityKeyIdentifier, String serialNumber, String notBefore, String notAfter, String signature);
@@ -120,11 +121,14 @@ class ArduinoIoTCloudTCP: public ArduinoIoTCloudClass
 
     enum class State
     {
+      ConfigPhy,
+      Init,
       ConnectPhy,
       SyncTime,
       ConnectMqttBroker,
       Connected,
       Disconnect,
+      Disconnected,
     };
 
     State _state;
@@ -133,11 +137,14 @@ class ArduinoIoTCloudTCP: public ArduinoIoTCloudClass
     ArduinoCloudThing _thing;
     ArduinoCloudDevice _device;
 
+    ArduinoIoTAuthenticationMode _authMode;
     String _brokerAddress;
     uint16_t _brokerPort;
     uint8_t _mqtt_data_buf[MQTT_TRANSMIT_BUFFER_SIZE];
     int _mqtt_data_len;
     bool _mqtt_data_request_retransmit;
+    bool _enable_watchdog;
+    bool _auto_reconnect;
 
 #if defined(BOARD_HAS_SECRET_KEY)
     String _password;
@@ -170,6 +177,8 @@ class ArduinoIoTCloudTCP: public ArduinoIoTCloudClass
     inline String getTopic_dataout  () { return ( getThingId().length() == 0) ? String("") : String("/a/t/" + getThingId() + "/e/o"); }
     inline String getTopic_datain   () { return ( getThingId().length() == 0) ? String("") : String("/a/t/" + getThingId() + "/e/i"); }
 
+    State handle_ConfigPhy();
+    State handle_Init();
     State handle_ConnectPhy();
     State handle_SyncTime();
     State handle_ConnectMqttBroker();