diff --git a/end2end-tests/cucumber-features/identity_creation.feature b/end2end-tests/cucumber-features/identity_creation.feature
index 2cc5bcc992aa0e33de26d303d6d8980a69d2a206..c2789a38e4f8a369ffb57fadef3f7874049eb8bd 100644
--- a/end2end-tests/cucumber-features/identity_creation.feature
+++ b/end2end-tests/cucumber-features/identity_creation.feature
@@ -5,11 +5,24 @@ Feature: Identity creation
     # - account creation fees (3 ÄžD) 
     # - existential deposit (2 ÄžD)
     # - transaction fees (below 1 ÄžD)
-    When alice sends 6 ÄžD to ferdie
+    When alice sends 6 ÄžD to dave
+    When alice sends 6 ÄžD to eve
     # alice last certification is counted from block zero
-    # then next cert can be done after cert_period
+    # then next cert can be done after cert_period, which is 15
     When 15 block later
-    When alice creates identity for ferdie
-    Then ferdie identity should be created
-    When ferdie confirms his identity with pseudo "Ferdie"
-    Then ferdie identity should be confirmed 
+    When alice creates identity for dave
+    Then dave identity should be created
+    Then dave should be certified by alice
+    When dave confirms his identity with pseudo "dave"
+    Then dave identity should be confirmed
+    When 3 block later
+    When bob certifies dave
+    When charlie certifies dave
+    Then dave should be certified by bob
+    Then dave should be certified by charlie
+    When 3 block later
+    # eve is not member, but has enough ÄžD to pay extrinsic fees
+    # I don't understand why she can not validate dave's identity 
+    # When eve validates dave identity
+    When alice validates dave identity
+    Then dave identity should be validated
diff --git a/end2end-tests/tests/common/identity.rs b/end2end-tests/tests/common/identity.rs
index 0f21cb5aa9896bee21f61ef521877b9fbb50a7e1..58e881264a4daed837fdc4bc0a039e3f2428f27c 100644
--- a/end2end-tests/tests/common/identity.rs
+++ b/end2end-tests/tests/common/identity.rs
@@ -72,8 +72,27 @@ pub async fn confirm_identity(client: &Client, from: AccountKeyring, pseudo: Str
     Ok(())
 }
 
-// get identity value from account keyring name
-pub async fn get_identity_value(world: &mut DuniterWorld, account: String) -> Result<IdtyValue> {
+pub async fn validate_identity(client: &Client, from: AccountKeyring, to: u32) -> Result<()> {
+    let from = PairSigner::new(from.pair());
+
+    let _events = create_block_with_extrinsic(
+        client,
+        client
+            .tx()
+            .create_signed(
+                &gdev::tx().identity().validate_identity(to),
+                &from,
+                BaseExtrinsicParamsBuilder::new(),
+            )
+            .await?,
+    )
+    .await?;
+
+    Ok(())
+}
+
+// get identity index from account keyring name
+pub async fn get_identity_index(world: &mut DuniterWorld, account: String) -> Result<u32> {
     let account = AccountKeyring::from_str(&account)
         .expect("unknown account")
         .to_account_id();
@@ -84,6 +103,12 @@ pub async fn get_identity_value(world: &mut DuniterWorld, account: String) -> Re
         .ok_or_else(|| anyhow::anyhow!("identity {} has no associated index", account))
         .unwrap();
 
+    Ok(identity_index)
+}
+// get identity value from account keyring name
+pub async fn get_identity_value(world: &mut DuniterWorld, account: String) -> Result<IdtyValue> {
+    let identity_index = get_identity_index(world, account).await.unwrap();
+
     let identity_value = world
         .read(&gdev::storage().identity().identities(identity_index))
         .await?
diff --git a/end2end-tests/tests/common/mod.rs b/end2end-tests/tests/common/mod.rs
index c1fd35076c0aa45fe5328cb1ebb436f7ea5f8d60..56ccdea5deadfb58b23dc74f2b7a818f245e3f97 100644
--- a/end2end-tests/tests/common/mod.rs
+++ b/end2end-tests/tests/common/mod.rs
@@ -21,7 +21,10 @@ pub mod cert;
 pub mod identity;
 pub mod oneshot;
 
-#[subxt::subxt(runtime_metadata_path = "../resources/metadata.scale")]
+#[subxt::subxt(
+    runtime_metadata_path = "../resources/metadata.scale",
+    derive_for_all_types = "Eq, PartialEq"
+)]
 pub mod gdev {}
 
 use anyhow::anyhow;
diff --git a/end2end-tests/tests/cucumber_tests.rs b/end2end-tests/tests/cucumber_tests.rs
index cd787603253c913076e04467c85e213339271c30..094932d4bfd8ac137e6bae82f74be7f69d379046 100644
--- a/end2end-tests/tests/cucumber_tests.rs
+++ b/end2end-tests/tests/cucumber_tests.rs
@@ -313,6 +313,15 @@ async fn confirm_identity(world: &mut DuniterWorld, from: String, pseudo: String
     common::identity::confirm_identity(world.client(), from, pseudo).await
 }
 
+#[when(regex = r#"([a-zA-Z]+) validates ([a-zA-Z]+) identity"#)]
+async fn validate_identity(world: &mut DuniterWorld, from: String, to: String) -> Result<()> {
+    // input names to keyrings
+    let from = AccountKeyring::from_str(&from).expect("unknown from");
+    let to: u32 = common::identity::get_identity_index(world, to).await?;
+
+    common::identity::validate_identity(world.client(), from, to).await
+}
+
 // ===== then ====
 
 #[then(regex = r"([a-zA-Z]+) should have (\d+) (ÄžD|cÄžD)")]
@@ -430,28 +439,30 @@ async fn should_be_certified_by(
 
 use gdev::runtime_types::pallet_identity::types::IdtyStatus;
 
-#[then(regex = r"([a-zA-Z]+) identity should be created")]
-async fn identity_should_be_created(world: &mut DuniterWorld, receiver: String) -> Result<()> {
-    let identity_value = common::identity::get_identity_value(world, receiver).await?;
+// status from string
+impl FromStr for IdtyStatus {
+    type Err = String;
 
-    match identity_value.status {
-        IdtyStatus::Created => Ok(()),
-        IdtyStatus::ConfirmedByOwner | IdtyStatus::Validated => {
-            Err(anyhow::anyhow!("status not created").into())
+    fn from_str(input: &str) -> std::result::Result<IdtyStatus, String> {
+        match input {
+            "created" => Ok(IdtyStatus::Created),
+            "confirmed" => Ok(IdtyStatus::ConfirmedByOwner),
+            "validated" => Ok(IdtyStatus::Validated),
+            _ => Err(format!("'{input}' does not match a status")),
         }
     }
 }
 
-#[then(regex = r"([a-zA-Z]+) identity should be confirmed")]
-async fn identity_should_be_confirmed(world: &mut DuniterWorld, name: String) -> Result<()> {
+#[then(regex = r"([a-zA-Z]+) identity should be ([a-zA-Z ]+)")]
+async fn identity_status_should_be(
+    world: &mut DuniterWorld,
+    name: String,
+    status: String,
+) -> Result<()> {
     let identity_value = common::identity::get_identity_value(world, name).await?;
-
-    match identity_value.status {
-        IdtyStatus::ConfirmedByOwner => Ok(()),
-        IdtyStatus::Created | IdtyStatus::Validated => {
-            Err(anyhow::anyhow!("status not confirmed by owner").into())
-        }
-    }
+    let expected_status = IdtyStatus::from_str(&status)?;
+    assert_eq!(identity_value.status, expected_status);
+    Ok(())
 }
 
 // ============================================================