diff --git a/end2end-tests/cucumber-features/identity_creation.feature b/end2end-tests/cucumber-features/identity_creation.feature
new file mode 100644
index 0000000000000000000000000000000000000000..376b6dec6b131e833d12e71f8f7747b64fe924fb
--- /dev/null
+++ b/end2end-tests/cucumber-features/identity_creation.feature
@@ -0,0 +1,7 @@
+Feature: Identity creation
+
+  Scenario: alice invites a new member to join the web of trust
+    When alice creates identity for ferdie
+    When alice creates identity for ferdie
+    When 2 block later
+    Then ferdie identity should be created
diff --git a/end2end-tests/tests/common/identity.rs b/end2end-tests/tests/common/identity.rs
new file mode 100644
index 0000000000000000000000000000000000000000..85d0fbcf74405ab0f9038374142f039ebaf4e2cc
--- /dev/null
+++ b/end2end-tests/tests/common/identity.rs
@@ -0,0 +1,45 @@
+// Copyright 2021 Axiom-Team
+//
+// This file is part of Substrate-Libre-Currency.
+//
+// Substrate-Libre-Currency is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, version 3 of the License.
+//
+// Substrate-Libre-Currency is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with Substrate-Libre-Currency. If not, see <https://www.gnu.org/licenses/>.
+
+use super::gdev;
+use super::gdev::runtime_types::pallet_identity;
+use super::*;
+use sp_keyring::AccountKeyring;
+use subxt::tx::PairSigner;
+
+pub async fn create_identity(
+    client: &Client,
+    from: AccountKeyring,
+    to: AccountKeyring,
+) -> Result<()> {
+    let from = PairSigner::new(from.pair());
+    let to = to.to_account_id();
+
+    let _events = create_block_with_extrinsic(
+        client,
+        client
+            .tx()
+            .create_signed(
+                &gdev::tx().identity().create_identity(to),
+                &from,
+                BaseExtrinsicParamsBuilder::new(),
+            )
+            .await?,
+    )
+    .await?;
+
+    Ok(())
+}
diff --git a/end2end-tests/tests/common/mod.rs b/end2end-tests/tests/common/mod.rs
index e9ae2cb03bd370123020c22d56a912e74675ea52..804306b5ab24bed0e1794f32b8f6c88b08b21c00 100644
--- a/end2end-tests/tests/common/mod.rs
+++ b/end2end-tests/tests/common/mod.rs
@@ -18,6 +18,7 @@
 
 pub mod balances;
 pub mod cert;
+pub mod identity;
 pub mod oneshot;
 
 #[subxt::subxt(runtime_metadata_path = "../resources/metadata.scale")]
diff --git a/end2end-tests/tests/cucumber_tests.rs b/end2end-tests/tests/cucumber_tests.rs
index f65142545775c92d7785ad5e1586b783b6493ec8..b821c95cfee83c676d07994a8f8d713622727ca2 100644
--- a/end2end-tests/tests/cucumber_tests.rs
+++ b/end2end-tests/tests/cucumber_tests.rs
@@ -297,6 +297,15 @@ async fn certifies(world: &mut DuniterWorld, from: String, to: String) -> Result
     common::cert::certify(world.client(), from, to).await
 }
 
+#[when(regex = r"([a-zA-Z]+) creates identity for ([a-zA-Z]+)")]
+async fn creates_identity(world: &mut DuniterWorld, from: String, to: String) -> Result<()> {
+    // Parse inputs
+    let from = AccountKeyring::from_str(&from).expect("unknown from");
+    let to = AccountKeyring::from_str(&to).expect("unknown to");
+
+    common::identity::create_identity(world.client(), from, to).await
+}
+
 // ===== then ====
 
 #[then(regex = r"([a-zA-Z]+) should have (\d+) (ÄžD|cÄžD)")]
@@ -377,6 +386,7 @@ async fn should_be_certified_by(
         .expect("unknown to")
         .to_account_id();
 
+    // get corresponding identities index
     let issuer_index = world
         .read(
             &gdev::storage()
@@ -398,6 +408,7 @@ async fn should_be_certified_by(
         .read_or_default(&gdev::storage().cert().certs_by_receiver(&receiver_index))
         .await?;
 
+    // look for certification by issuer/receiver pair
     match issuers.binary_search_by(|(issuer_, _)| issuer_index.cmp(issuer_)) {
         Ok(_) => Ok(()),
         Err(_) => Err(anyhow::anyhow!(
@@ -410,6 +421,36 @@ async fn should_be_certified_by(
     }
 }
 
+#[then(regex = r"([a-zA-Z]+) identity should be created")]
+async fn identity_should_be_created(world: &mut DuniterWorld, receiver: String) -> Result<()> {
+    // Parse inputs
+    let receiver_account = AccountKeyring::from_str(&receiver)
+        .expect("unknown to")
+        .to_account_id();
+
+    let receiver_index = world
+        .read(
+            &gdev::storage()
+                .identity()
+                .identity_index_of(&receiver_account),
+        )
+        .await?
+        .ok_or_else(|| anyhow::anyhow!("identity {} has no associated index", receiver))
+        .unwrap();
+
+    let _identity_value = world
+        .read(&gdev::storage().identity().identities(&receiver_index))
+        .await?
+        .ok_or_else(|| {
+            anyhow::anyhow!(
+                "indentity index {} does not have associated value",
+                receiver_index
+            )
+        })?;
+
+    Ok(())
+}
+
 // ============================================================
 
 #[derive(clap::Args)]