diff --git a/prdoc/pr_4168.prdoc b/prdoc/pr_4168.prdoc
new file mode 100644
index 0000000000000000000000000000000000000000..9a498500f08b4f7013c3b2829d87b7e588829406
--- /dev/null
+++ b/prdoc/pr_4168.prdoc
@@ -0,0 +1,8 @@
+title: Stabilize chianHead RPC class to version 1
+
+doc:
+  - audience: Node Dev
+    description: |
+      The chainHead RPC API is stabilized to version 1.
+
+crates: [ ]
diff --git a/substrate/client/rpc-spec-v2/src/chain_head/api.rs b/substrate/client/rpc-spec-v2/src/chain_head/api.rs
index 3851adac2644d09b824383dba2e80d6877eff01a..23cb0bbf54585383e797a452d9747cac36649ad1 100644
--- a/substrate/client/rpc-spec-v2/src/chain_head/api.rs
+++ b/substrate/client/rpc-spec-v2/src/chain_head/api.rs
@@ -37,15 +37,15 @@ pub trait ChainHeadApi<Hash> {
 	///
 	/// This method is unstable and subject to change in the future.
 	#[subscription(
-		name = "chainHead_unstable_follow" => "chainHead_unstable_followEvent",
-		unsubscribe = "chainHead_unstable_unfollow",
+		name = "chainHead_v1_follow" => "chainHead_v1_followEvent",
+		unsubscribe = "chainHead_v1_unfollow",
 		item = FollowEvent<Hash>,
 	)]
 	fn chain_head_unstable_follow(&self, with_runtime: bool);
 
 	/// Retrieves the body (list of transactions) of a pinned block.
 	///
-	/// This method should be seen as a complement to `chainHead_unstable_follow`,
+	/// This method should be seen as a complement to `chainHead_v1_follow`,
 	/// allowing the JSON-RPC client to retrieve more information about a block
 	/// that has been reported.
 	///
@@ -54,7 +54,7 @@ pub trait ChainHeadApi<Hash> {
 	/// # Unstable
 	///
 	/// This method is unstable and subject to change in the future.
-	#[method(name = "chainHead_unstable_body", raw_method)]
+	#[method(name = "chainHead_v1_body", raw_method)]
 	async fn chain_head_unstable_body(
 		&self,
 		follow_subscription: String,
@@ -63,7 +63,7 @@ pub trait ChainHeadApi<Hash> {
 
 	/// Retrieves the header of a pinned block.
 	///
-	/// This method should be seen as a complement to `chainHead_unstable_follow`,
+	/// This method should be seen as a complement to `chainHead_v1_follow`,
 	/// allowing the JSON-RPC client to retrieve more information about a block
 	/// that has been reported.
 	///
@@ -73,7 +73,7 @@ pub trait ChainHeadApi<Hash> {
 	/// # Unstable
 	///
 	/// This method is unstable and subject to change in the future.
-	#[method(name = "chainHead_unstable_header", raw_method)]
+	#[method(name = "chainHead_v1_header", raw_method)]
 	async fn chain_head_unstable_header(
 		&self,
 		follow_subscription: String,
@@ -85,7 +85,7 @@ pub trait ChainHeadApi<Hash> {
 	/// # Unstable
 	///
 	/// This method is unstable and subject to change in the future.
-	#[method(name = "chainHead_unstable_storage", raw_method)]
+	#[method(name = "chainHead_v1_storage", raw_method)]
 	async fn chain_head_unstable_storage(
 		&self,
 		follow_subscription: String,
@@ -99,7 +99,7 @@ pub trait ChainHeadApi<Hash> {
 	/// # Unstable
 	///
 	/// This method is unstable and subject to change in the future.
-	#[method(name = "chainHead_unstable_call", raw_method)]
+	#[method(name = "chainHead_v1_call", raw_method)]
 	async fn chain_head_unstable_call(
 		&self,
 		follow_subscription: String,
@@ -118,7 +118,7 @@ pub trait ChainHeadApi<Hash> {
 	/// # Unstable
 	///
 	/// This method is unstable and subject to change in the future.
-	#[method(name = "chainHead_unstable_unpin", raw_method)]
+	#[method(name = "chainHead_v1_unpin", raw_method)]
 	async fn chain_head_unstable_unpin(
 		&self,
 		follow_subscription: String,
@@ -131,21 +131,21 @@ pub trait ChainHeadApi<Hash> {
 	/// # Unstable
 	///
 	/// This method is unstable and subject to change in the future.
-	#[method(name = "chainHead_unstable_continue", raw_method)]
+	#[method(name = "chainHead_v1_continue", raw_method)]
 	async fn chain_head_unstable_continue(
 		&self,
 		follow_subscription: String,
 		operation_id: String,
 	) -> Result<(), Error>;
 
-	/// Stops an operation started with chainHead_unstable_body, chainHead_unstable_call, or
-	/// chainHead_unstable_storage. If the operation was still in progress, this interrupts it. If
+	/// Stops an operation started with chainHead_v1_body, chainHead_v1_call, or
+	/// chainHead_v1_storage. If the operation was still in progress, this interrupts it. If
 	/// the operation was already finished, this call has no effect.
 	///
 	/// # Unstable
 	///
 	/// This method is unstable and subject to change in the future.
-	#[method(name = "chainHead_unstable_stopOperation", raw_method)]
+	#[method(name = "chainHead_v1_stopOperation", raw_method)]
 	async fn chain_head_unstable_stop_operation(
 		&self,
 		follow_subscription: String,
diff --git a/substrate/client/rpc-spec-v2/src/chain_head/subscription/inner.rs b/substrate/client/rpc-spec-v2/src/chain_head/subscription/inner.rs
index 0e5ccb91d39a61a76a850119590699d563d88df8..3495d9e54490c0214c75359843ce0bbf42aa5465 100644
--- a/substrate/client/rpc-spec-v2/src/chain_head/subscription/inner.rs
+++ b/substrate/client/rpc-spec-v2/src/chain_head/subscription/inner.rs
@@ -186,7 +186,7 @@ impl OperationState {
 	/// Stops the operation if `waitingForContinue` event was emitted for the associated
 	/// operation ID.
 	///
-	/// Returns nothing in accordance with `chainHead_unstable_stopOperation`.
+	/// Returns nothing in accordance with `chainHead_v1_stopOperation`.
 	pub fn stop_operation(&self) {
 		// `waitingForContinue` not generated.
 		if !self.shared_state.requested_continue.load(std::sync::atomic::Ordering::Acquire) {
diff --git a/substrate/client/rpc-spec-v2/src/chain_head/tests.rs b/substrate/client/rpc-spec-v2/src/chain_head/tests.rs
index 14f664858a0d8318dfb2f1607206c0b6075754fd..4bab2194e082acf65e47bd0a3917ee23c2a81884 100644
--- a/substrate/client/rpc-spec-v2/src/chain_head/tests.rs
+++ b/substrate/client/rpc-spec-v2/src/chain_head/tests.rs
@@ -156,7 +156,7 @@ async fn setup_api() -> (
 	)
 	.into_rpc();
 
-	let mut sub = api.subscribe_unbounded("chainHead_unstable_follow", [true]).await.unwrap();
+	let mut sub = api.subscribe_unbounded("chainHead_v1_follow", [true]).await.unwrap();
 	let sub_id = sub.subscription_id();
 	let sub_id = serde_json::to_string(&sub_id).unwrap();
 
@@ -266,7 +266,7 @@ async fn follow_subscription_produces_blocks() {
 	.into_rpc();
 
 	let finalized_hash = client.info().finalized_hash;
-	let mut sub = api.subscribe_unbounded("chainHead_unstable_follow", [false]).await.unwrap();
+	let mut sub = api.subscribe_unbounded("chainHead_v1_follow", [false]).await.unwrap();
 
 	// Initialized must always be reported first.
 	let event: FollowEvent<String> = get_next_event(&mut sub).await;
@@ -337,7 +337,7 @@ async fn follow_with_runtime() {
 	.into_rpc();
 
 	let finalized_hash = client.info().finalized_hash;
-	let mut sub = api.subscribe_unbounded("chainHead_unstable_follow", [true]).await.unwrap();
+	let mut sub = api.subscribe_unbounded("chainHead_v1_follow", [true]).await.unwrap();
 
 	// Initialized must always be reported first.
 	let event: FollowEvent<String> = get_next_event(&mut sub).await;
@@ -448,14 +448,14 @@ async fn get_header() {
 
 	// Invalid subscription ID must produce no results.
 	let res: Option<String> = api
-		.call("chainHead_unstable_header", ["invalid_sub_id", &invalid_hash])
+		.call("chainHead_v1_header", ["invalid_sub_id", &invalid_hash])
 		.await
 		.unwrap();
 	assert!(res.is_none());
 
 	// Valid subscription with invalid block hash will error.
 	let err = api
-		.call::<_, serde_json::Value>("chainHead_unstable_header", [&sub_id, &invalid_hash])
+		.call::<_, serde_json::Value>("chainHead_v1_header", [&sub_id, &invalid_hash])
 		.await
 		.unwrap_err();
 	assert_matches!(err,
@@ -463,7 +463,7 @@ async fn get_header() {
 	);
 
 	// Obtain the valid header.
-	let res: String = api.call("chainHead_unstable_header", [&sub_id, &block_hash]).await.unwrap();
+	let res: String = api.call("chainHead_v1_header", [&sub_id, &block_hash]).await.unwrap();
 	let bytes = array_bytes::hex2bytes(&res).unwrap();
 	let header: Header = Decode::decode(&mut &bytes[..]).unwrap();
 	assert_eq!(header, block.header);
@@ -476,15 +476,13 @@ async fn get_body() {
 	let invalid_hash = hex_string(&INVALID_HASH);
 
 	// Subscription ID is invalid.
-	let response: MethodResponse = api
-		.call("chainHead_unstable_body", ["invalid_sub_id", &invalid_hash])
-		.await
-		.unwrap();
+	let response: MethodResponse =
+		api.call("chainHead_v1_body", ["invalid_sub_id", &invalid_hash]).await.unwrap();
 	assert_matches!(response, MethodResponse::LimitReached);
 
 	// Block hash is invalid.
 	let err = api
-		.call::<_, serde_json::Value>("chainHead_unstable_body", [&sub_id, &invalid_hash])
+		.call::<_, serde_json::Value>("chainHead_v1_body", [&sub_id, &invalid_hash])
 		.await
 		.unwrap_err();
 	assert_matches!(err,
@@ -493,7 +491,7 @@ async fn get_body() {
 
 	// Valid call.
 	let response: MethodResponse =
-		api.call("chainHead_unstable_body", [&sub_id, &block_hash]).await.unwrap();
+		api.call("chainHead_v1_body", [&sub_id, &block_hash]).await.unwrap();
 	let operation_id = match response {
 		MethodResponse::Started(started) => started.operation_id,
 		MethodResponse::LimitReached => panic!("Expected started response"),
@@ -534,7 +532,7 @@ async fn get_body() {
 
 	// Valid call to a block with extrinsics.
 	let response: MethodResponse =
-		api.call("chainHead_unstable_body", [&sub_id, &block_hash]).await.unwrap();
+		api.call("chainHead_v1_body", [&sub_id, &block_hash]).await.unwrap();
 	let operation_id = match response {
 		MethodResponse::Started(started) => started.operation_id,
 		MethodResponse::LimitReached => panic!("Expected started response"),
@@ -556,10 +554,7 @@ async fn call_runtime() {
 
 	// Subscription ID is invalid.
 	let response: MethodResponse = api
-		.call(
-			"chainHead_unstable_call",
-			["invalid_sub_id", &block_hash, "BabeApi_current_epoch", "0x00"],
-		)
+		.call("chainHead_v1_call", ["invalid_sub_id", &block_hash, "BabeApi_current_epoch", "0x00"])
 		.await
 		.unwrap();
 	assert_matches!(response, MethodResponse::LimitReached);
@@ -567,7 +562,7 @@ async fn call_runtime() {
 	// Block hash is invalid.
 	let err = api
 		.call::<_, serde_json::Value>(
-			"chainHead_unstable_call",
+			"chainHead_v1_call",
 			[&sub_id, &invalid_hash, "BabeApi_current_epoch", "0x00"],
 		)
 		.await
@@ -579,7 +574,7 @@ async fn call_runtime() {
 	// Pass an invalid parameters that cannot be decode.
 	let err = api
 		.call::<_, serde_json::Value>(
-			"chainHead_unstable_call",
+			"chainHead_v1_call",
 			// 0x0 is invalid.
 			[&sub_id, &block_hash, "BabeApi_current_epoch", "0x0"],
 		)
@@ -595,7 +590,7 @@ async fn call_runtime() {
 	let call_parameters = hex_string(&alice_id.encode());
 	let response: MethodResponse = api
 		.call(
-			"chainHead_unstable_call",
+			"chainHead_v1_call",
 			[&sub_id, &block_hash, "AccountNonceApi_account_nonce", &call_parameters],
 		)
 		.await
@@ -614,7 +609,7 @@ async fn call_runtime() {
 	// The `current_epoch` takes no parameters and not draining the input buffer
 	// will cause the execution to fail.
 	let response: MethodResponse = api
-		.call("chainHead_unstable_call", [&sub_id, &block_hash, "BabeApi_current_epoch", "0x00"])
+		.call("chainHead_v1_call", [&sub_id, &block_hash, "BabeApi_current_epoch", "0x00"])
 		.await
 		.unwrap();
 	let operation_id = match response {
@@ -651,7 +646,7 @@ async fn call_runtime_without_flag() {
 	)
 	.into_rpc();
 
-	let mut sub = api.subscribe_unbounded("chainHead_unstable_follow", [false]).await.unwrap();
+	let mut sub = api.subscribe_unbounded("chainHead_v1_follow", [false]).await.unwrap();
 	let sub_id = sub.subscription_id();
 	let sub_id = serde_json::to_string(&sub_id).unwrap();
 
@@ -685,7 +680,7 @@ async fn call_runtime_without_flag() {
 	let call_parameters = hex_string(&alice_id.encode());
 	let err = api
 		.call::<_, serde_json::Value>(
-			"chainHead_unstable_call",
+			"chainHead_v1_call",
 			[&sub_id, &block_hash, "AccountNonceApi_account_nonce", &call_parameters],
 		)
 		.await
@@ -706,7 +701,7 @@ async fn get_storage_hash() {
 	// Subscription ID is invalid.
 	let response: MethodResponse = api
 		.call(
-			"chainHead_unstable_storage",
+			"chainHead_v1_storage",
 			rpc_params![
 				"invalid_sub_id",
 				&invalid_hash,
@@ -720,7 +715,7 @@ async fn get_storage_hash() {
 	// Block hash is invalid.
 	let err = api
 		.call::<_, serde_json::Value>(
-			"chainHead_unstable_storage",
+			"chainHead_v1_storage",
 			rpc_params![
 				&sub_id,
 				&invalid_hash,
@@ -736,7 +731,7 @@ async fn get_storage_hash() {
 	// Valid call without storage at the key.
 	let response: MethodResponse = api
 		.call(
-			"chainHead_unstable_storage",
+			"chainHead_v1_storage",
 			rpc_params![
 				&sub_id,
 				&block_hash,
@@ -779,7 +774,7 @@ async fn get_storage_hash() {
 	// Valid call with storage at the key.
 	let response: MethodResponse = api
 		.call(
-			"chainHead_unstable_storage",
+			"chainHead_v1_storage",
 			rpc_params![
 				&sub_id,
 				&block_hash,
@@ -812,7 +807,7 @@ async fn get_storage_hash() {
 	// Valid call with storage at the key.
 	let response: MethodResponse = api
 		.call(
-			"chainHead_unstable_storage",
+			"chainHead_v1_storage",
 			rpc_params![
 				&sub_id,
 				&genesis_hash,
@@ -869,7 +864,7 @@ async fn get_storage_multi_query_iter() {
 	// Valid call with storage at the key.
 	let response: MethodResponse = api
 		.call(
-			"chainHead_unstable_storage",
+			"chainHead_v1_storage",
 			rpc_params![
 				&sub_id,
 				&block_hash,
@@ -920,7 +915,7 @@ async fn get_storage_multi_query_iter() {
 	let expected_value = hex_string(&CHILD_VALUE);
 	let response: MethodResponse = api
 		.call(
-			"chainHead_unstable_storage",
+			"chainHead_v1_storage",
 			rpc_params![
 				&sub_id,
 				&genesis_hash,
@@ -974,7 +969,7 @@ async fn get_storage_value() {
 	// Subscription ID is invalid.
 	let response: MethodResponse = api
 		.call(
-			"chainHead_unstable_storage",
+			"chainHead_v1_storage",
 			rpc_params![
 				"invalid_sub_id",
 				&invalid_hash,
@@ -988,7 +983,7 @@ async fn get_storage_value() {
 	// Block hash is invalid.
 	let err = api
 		.call::<_, serde_json::Value>(
-			"chainHead_unstable_storage",
+			"chainHead_v1_storage",
 			rpc_params![
 				&sub_id,
 				&invalid_hash,
@@ -1004,7 +999,7 @@ async fn get_storage_value() {
 	// Valid call without storage at the key.
 	let response: MethodResponse = api
 		.call(
-			"chainHead_unstable_storage",
+			"chainHead_v1_storage",
 			rpc_params![
 				&sub_id,
 				&block_hash,
@@ -1047,7 +1042,7 @@ async fn get_storage_value() {
 	// Valid call with storage at the key.
 	let response: MethodResponse = api
 		.call(
-			"chainHead_unstable_storage",
+			"chainHead_v1_storage",
 			rpc_params![
 				&sub_id,
 				&block_hash,
@@ -1079,7 +1074,7 @@ async fn get_storage_value() {
 
 	let response: MethodResponse = api
 		.call(
-			"chainHead_unstable_storage",
+			"chainHead_v1_storage",
 			rpc_params![
 				&sub_id,
 				&genesis_hash,
@@ -1121,7 +1116,7 @@ async fn get_storage_non_queryable_key() {
 
 	let response: MethodResponse = api
 		.call(
-			"chainHead_unstable_storage",
+			"chainHead_v1_storage",
 			rpc_params![
 				&sub_id,
 				&block_hash,
@@ -1146,7 +1141,7 @@ async fn get_storage_non_queryable_key() {
 	let prefixed_key = hex_string(&prefixed_key);
 	let response: MethodResponse = api
 		.call(
-			"chainHead_unstable_storage",
+			"chainHead_v1_storage",
 			rpc_params![
 				&sub_id,
 				&block_hash,
@@ -1171,7 +1166,7 @@ async fn get_storage_non_queryable_key() {
 	let prefixed_key = hex_string(&prefixed_key);
 	let response: MethodResponse = api
 		.call(
-			"chainHead_unstable_storage",
+			"chainHead_v1_storage",
 			rpc_params![
 				&sub_id,
 				&block_hash,
@@ -1197,7 +1192,7 @@ async fn get_storage_non_queryable_key() {
 	let prefixed_key = hex_string(&prefixed_key);
 	let response: MethodResponse = api
 		.call(
-			"chainHead_unstable_storage",
+			"chainHead_v1_storage",
 			rpc_params![
 				&sub_id,
 				&block_hash,
@@ -1227,9 +1222,9 @@ async fn unique_operation_ids() {
 
 	// Ensure that operation IDs are unique for multiple method calls.
 	for _ in 0..5 {
-		// Valid `chainHead_unstable_body` call.
+		// Valid `chainHead_v1_body` call.
 		let response: MethodResponse =
-			api.call("chainHead_unstable_body", [&sub_id, &block_hash]).await.unwrap();
+			api.call("chainHead_v1_body", [&sub_id, &block_hash]).await.unwrap();
 		let operation_id = match response {
 			MethodResponse::Started(started) => started.operation_id,
 			MethodResponse::LimitReached => panic!("Expected started response"),
@@ -1241,11 +1236,11 @@ async fn unique_operation_ids() {
 		// Ensure uniqueness.
 		assert!(op_ids.insert(operation_id));
 
-		// Valid `chainHead_unstable_storage` call.
+		// Valid `chainHead_v1_storage` call.
 		let key = hex_string(&KEY);
 		let response: MethodResponse = api
 			.call(
-				"chainHead_unstable_storage",
+				"chainHead_v1_storage",
 				rpc_params![
 					&sub_id,
 					&block_hash,
@@ -1266,12 +1261,12 @@ async fn unique_operation_ids() {
 		// Ensure uniqueness.
 		assert!(op_ids.insert(operation_id));
 
-		// Valid `chainHead_unstable_call` call.
+		// Valid `chainHead_v1_call` call.
 		let alice_id = AccountKeyring::Alice.to_account_id();
 		let call_parameters = hex_string(&alice_id.encode());
 		let response: MethodResponse = api
 			.call(
-				"chainHead_unstable_call",
+				"chainHead_v1_call",
 				[&sub_id, &block_hash, "AccountNonceApi_account_nonce", &call_parameters],
 			)
 			.await
@@ -1313,12 +1308,11 @@ async fn separate_operation_ids_for_subscriptions() {
 	.into_rpc();
 
 	// Create two separate subscriptions.
-	let mut sub_first = api.subscribe_unbounded("chainHead_unstable_follow", [true]).await.unwrap();
+	let mut sub_first = api.subscribe_unbounded("chainHead_v1_follow", [true]).await.unwrap();
 	let sub_id_first = sub_first.subscription_id();
 	let sub_id_first = serde_json::to_string(&sub_id_first).unwrap();
 
-	let mut sub_second =
-		api.subscribe_unbounded("chainHead_unstable_follow", [true]).await.unwrap();
+	let mut sub_second = api.subscribe_unbounded("chainHead_v1_follow", [true]).await.unwrap();
 	let sub_id_second = sub_second.subscription_id();
 	let sub_id_second = serde_json::to_string(&sub_id_second).unwrap();
 
@@ -1362,17 +1356,15 @@ async fn separate_operation_ids_for_subscriptions() {
 
 	// Each `chainHead_follow` subscription receives a separate operation ID.
 	let response: MethodResponse =
-		api.call("chainHead_unstable_body", [&sub_id_first, &block_hash]).await.unwrap();
+		api.call("chainHead_v1_body", [&sub_id_first, &block_hash]).await.unwrap();
 	let operation_id: String = match response {
 		MethodResponse::Started(started) => started.operation_id,
 		MethodResponse::LimitReached => panic!("Expected started response"),
 	};
 	assert_eq!(operation_id, "0");
 
-	let response: MethodResponse = api
-		.call("chainHead_unstable_body", [&sub_id_second, &block_hash])
-		.await
-		.unwrap();
+	let response: MethodResponse =
+		api.call("chainHead_v1_body", [&sub_id_second, &block_hash]).await.unwrap();
 	let operation_id_second: String = match response {
 		MethodResponse::Started(started) => started.operation_id,
 		MethodResponse::LimitReached => panic!("Expected started response"),
@@ -1449,7 +1441,7 @@ async fn follow_generates_initial_blocks() {
 	let block_2_f_hash = block_2_f.header.hash();
 	client.import(BlockOrigin::Own, block_2_f.clone()).await.unwrap();
 
-	let mut sub = api.subscribe_unbounded("chainHead_unstable_follow", [false]).await.unwrap();
+	let mut sub = api.subscribe_unbounded("chainHead_v1_follow", [false]).await.unwrap();
 
 	// Initialized must always be reported first.
 	let event: FollowEvent<String> = get_next_event(&mut sub).await;
@@ -1561,7 +1553,7 @@ async fn follow_exceeding_pinned_blocks() {
 	)
 	.into_rpc();
 
-	let mut sub = api.subscribe_unbounded("chainHead_unstable_follow", [false]).await.unwrap();
+	let mut sub = api.subscribe_unbounded("chainHead_v1_follow", [false]).await.unwrap();
 
 	let block = BlockBuilderBuilder::new(&*client)
 		.on_parent_block(client.chain_info().genesis_hash)
@@ -1640,7 +1632,7 @@ async fn follow_with_unpin() {
 	)
 	.into_rpc();
 
-	let mut sub = api.subscribe_unbounded("chainHead_unstable_follow", [false]).await.unwrap();
+	let mut sub = api.subscribe_unbounded("chainHead_v1_follow", [false]).await.unwrap();
 	let sub_id = sub.subscription_id();
 	let sub_id = serde_json::to_string(&sub_id).unwrap();
 
@@ -1672,17 +1664,14 @@ async fn follow_with_unpin() {
 	// Unpin an invalid subscription ID must return Ok(()).
 	let invalid_hash = hex_string(&INVALID_HASH);
 	let _res: () = api
-		.call("chainHead_unstable_unpin", rpc_params!["invalid_sub_id", &invalid_hash])
+		.call("chainHead_v1_unpin", rpc_params!["invalid_sub_id", &invalid_hash])
 		.await
 		.unwrap();
 
 	// Valid subscription with invalid block hash.
 	let invalid_hash = hex_string(&INVALID_HASH);
 	let err = api
-		.call::<_, serde_json::Value>(
-			"chainHead_unstable_unpin",
-			rpc_params![&sub_id, &invalid_hash],
-		)
+		.call::<_, serde_json::Value>("chainHead_v1_unpin", rpc_params![&sub_id, &invalid_hash])
 		.await
 		.unwrap_err();
 	assert_matches!(err,
@@ -1690,10 +1679,7 @@ async fn follow_with_unpin() {
 	);
 
 	// To not exceed the number of pinned blocks, we need to unpin before the next import.
-	let _res: () = api
-		.call("chainHead_unstable_unpin", rpc_params![&sub_id, &block_hash])
-		.await
-		.unwrap();
+	let _res: () = api.call("chainHead_v1_unpin", rpc_params![&sub_id, &block_hash]).await.unwrap();
 
 	// Block tree:
 	//   finalized_block -> block -> block2
@@ -1754,7 +1740,7 @@ async fn unpin_duplicate_hashes() {
 	)
 	.into_rpc();
 
-	let mut sub = api.subscribe_unbounded("chainHead_unstable_follow", [false]).await.unwrap();
+	let mut sub = api.subscribe_unbounded("chainHead_v1_follow", [false]).await.unwrap();
 	let sub_id = sub.subscription_id();
 	let sub_id = serde_json::to_string(&sub_id).unwrap();
 
@@ -1786,7 +1772,7 @@ async fn unpin_duplicate_hashes() {
 	// Try to unpin duplicate hashes.
 	let err = api
 		.call::<_, serde_json::Value>(
-			"chainHead_unstable_unpin",
+			"chainHead_v1_unpin",
 			rpc_params![&sub_id, vec![&block_hash, &block_hash]],
 		)
 		.await
@@ -1821,7 +1807,7 @@ async fn unpin_duplicate_hashes() {
 	// Try to unpin duplicate hashes.
 	let err = api
 		.call::<_, serde_json::Value>(
-			"chainHead_unstable_unpin",
+			"chainHead_v1_unpin",
 			rpc_params![&sub_id, vec![&block_hash, &block_hash_2, &block_hash]],
 		)
 		.await
@@ -1832,7 +1818,7 @@ async fn unpin_duplicate_hashes() {
 
 	// Can unpin blocks.
 	let _res: () = api
-		.call("chainHead_unstable_unpin", rpc_params![&sub_id, vec![&block_hash, &block_hash_2]])
+		.call("chainHead_v1_unpin", rpc_params![&sub_id, vec![&block_hash, &block_hash_2]])
 		.await
 		.unwrap();
 }
@@ -1859,7 +1845,7 @@ async fn follow_with_multiple_unpin_hashes() {
 	)
 	.into_rpc();
 
-	let mut sub = api.subscribe_unbounded("chainHead_unstable_follow", [false]).await.unwrap();
+	let mut sub = api.subscribe_unbounded("chainHead_v1_follow", [false]).await.unwrap();
 	let sub_id = sub.subscription_id();
 	let sub_id = serde_json::to_string(&sub_id).unwrap();
 
@@ -1930,16 +1916,13 @@ async fn follow_with_multiple_unpin_hashes() {
 	// Unpin an invalid subscription ID must return Ok(()).
 	let invalid_hash = hex_string(&INVALID_HASH);
 	let _res: () = api
-		.call("chainHead_unstable_unpin", rpc_params!["invalid_sub_id", &invalid_hash])
+		.call("chainHead_v1_unpin", rpc_params!["invalid_sub_id", &invalid_hash])
 		.await
 		.unwrap();
 
 	// Valid subscription with invalid block hash.
 	let err = api
-		.call::<_, serde_json::Value>(
-			"chainHead_unstable_unpin",
-			rpc_params![&sub_id, &invalid_hash],
-		)
+		.call::<_, serde_json::Value>("chainHead_v1_unpin", rpc_params![&sub_id, &invalid_hash])
 		.await
 		.unwrap_err();
 	assert_matches!(err,
@@ -1947,14 +1930,14 @@ async fn follow_with_multiple_unpin_hashes() {
 	);
 
 	let _res: () = api
-		.call("chainHead_unstable_unpin", rpc_params![&sub_id, &block_1_hash])
+		.call("chainHead_v1_unpin", rpc_params![&sub_id, &block_1_hash])
 		.await
 		.unwrap();
 
 	// One block hash is invalid. Block 1 is already unpinned.
 	let err = api
 		.call::<_, serde_json::Value>(
-			"chainHead_unstable_unpin",
+			"chainHead_v1_unpin",
 			rpc_params![&sub_id, vec![&block_1_hash, &block_2_hash, &block_3_hash]],
 		)
 		.await
@@ -1965,16 +1948,13 @@ async fn follow_with_multiple_unpin_hashes() {
 
 	// Unpin multiple blocks.
 	let _res: () = api
-		.call("chainHead_unstable_unpin", rpc_params![&sub_id, vec![&block_2_hash, &block_3_hash]])
+		.call("chainHead_v1_unpin", rpc_params![&sub_id, vec![&block_2_hash, &block_3_hash]])
 		.await
 		.unwrap();
 
 	// Check block 2 and 3 are unpinned.
 	let err = api
-		.call::<_, serde_json::Value>(
-			"chainHead_unstable_unpin",
-			rpc_params![&sub_id, &block_2_hash],
-		)
+		.call::<_, serde_json::Value>("chainHead_v1_unpin", rpc_params![&sub_id, &block_2_hash])
 		.await
 		.unwrap_err();
 	assert_matches!(err,
@@ -1982,10 +1962,7 @@ async fn follow_with_multiple_unpin_hashes() {
 	);
 
 	let err = api
-		.call::<_, serde_json::Value>(
-			"chainHead_unstable_unpin",
-			rpc_params![&sub_id, &block_3_hash],
-		)
+		.call::<_, serde_json::Value>("chainHead_v1_unpin", rpc_params![&sub_id, &block_3_hash])
 		.await
 		.unwrap_err();
 	assert_matches!(err,
@@ -2016,7 +1993,7 @@ async fn follow_prune_best_block() {
 	.into_rpc();
 
 	let finalized_hash = client.info().finalized_hash;
-	let mut sub = api.subscribe_unbounded("chainHead_unstable_follow", [false]).await.unwrap();
+	let mut sub = api.subscribe_unbounded("chainHead_v1_follow", [false]).await.unwrap();
 
 	// Initialized must always be reported first.
 	let event: FollowEvent<String> = get_next_event(&mut sub).await;
@@ -2178,7 +2155,7 @@ async fn follow_prune_best_block() {
 	let sub_id = sub.subscription_id();
 	let sub_id = serde_json::to_string(&sub_id).unwrap();
 	let hash = format!("{:?}", block_2_hash);
-	let _res: () = api.call("chainHead_unstable_unpin", rpc_params![&sub_id, &hash]).await.unwrap();
+	let _res: () = api.call("chainHead_v1_unpin", rpc_params![&sub_id, &hash]).await.unwrap();
 }
 
 #[tokio::test]
@@ -2282,7 +2259,7 @@ async fn follow_forks_pruned_block() {
 	// Block 2_f and 3_f are not pruned, pruning happens at height (N - 1).
 	client.finalize_block(block_3_hash, None).unwrap();
 
-	let mut sub = api.subscribe_unbounded("chainHead_unstable_follow", [false]).await.unwrap();
+	let mut sub = api.subscribe_unbounded("chainHead_v1_follow", [false]).await.unwrap();
 
 	// Initialized must always be reported first.
 	let event: FollowEvent<String> = get_next_event(&mut sub).await;
@@ -2444,7 +2421,7 @@ async fn follow_report_multiple_pruned_block() {
 	let block_3_f = block_builder.build().unwrap().block;
 	let block_3_f_hash = block_3_f.hash();
 	client.import(BlockOrigin::Own, block_3_f.clone()).await.unwrap();
-	let mut sub = api.subscribe_unbounded("chainHead_unstable_follow", [false]).await.unwrap();
+	let mut sub = api.subscribe_unbounded("chainHead_v1_follow", [false]).await.unwrap();
 
 	// Initialized must always be reported first.
 	let event: FollowEvent<String> = get_next_event(&mut sub).await;
@@ -2630,7 +2607,7 @@ async fn pin_block_references() {
 		}
 	}
 
-	let mut sub = api.subscribe_unbounded("chainHead_unstable_follow", [false]).await.unwrap();
+	let mut sub = api.subscribe_unbounded("chainHead_v1_follow", [false]).await.unwrap();
 	let sub_id = sub.subscription_id();
 	let sub_id = serde_json::to_string(&sub_id).unwrap();
 
@@ -2669,10 +2646,7 @@ async fn pin_block_references() {
 	wait_pinned_references(&backend, &hash, 1).await;
 
 	// To not exceed the number of pinned blocks, we need to unpin before the next import.
-	let _res: () = api
-		.call("chainHead_unstable_unpin", rpc_params![&sub_id, &block_hash])
-		.await
-		.unwrap();
+	let _res: () = api.call("chainHead_v1_unpin", rpc_params![&sub_id, &block_hash]).await.unwrap();
 
 	// Make sure unpin clears out the reference.
 	let refs = backend.pin_refs(&hash).unwrap();
@@ -2765,7 +2739,7 @@ async fn follow_finalized_before_new_block() {
 	let block_1_hash = block_1.header.hash();
 	client.import(BlockOrigin::Own, block_1.clone()).await.unwrap();
 
-	let mut sub = api.subscribe_unbounded("chainHead_unstable_follow", [false]).await.unwrap();
+	let mut sub = api.subscribe_unbounded("chainHead_v1_follow", [false]).await.unwrap();
 
 	// Trigger the `FinalizedNotification` for block 1 before the `BlockImportNotification`, and
 	// expect for the `chainHead` to generate `NewBlock`, `BestBlock` and `Finalized` events.
@@ -2870,7 +2844,7 @@ async fn ensure_operation_limits_works() {
 	)
 	.into_rpc();
 
-	let mut sub = api.subscribe_unbounded("chainHead_unstable_follow", [true]).await.unwrap();
+	let mut sub = api.subscribe_unbounded("chainHead_v1_follow", [true]).await.unwrap();
 	let sub_id = sub.subscription_id();
 	let sub_id = serde_json::to_string(&sub_id).unwrap();
 
@@ -2909,7 +2883,7 @@ async fn ensure_operation_limits_works() {
 	];
 
 	let response: MethodResponse = api
-		.call("chainHead_unstable_storage", rpc_params![&sub_id, &block_hash, items])
+		.call("chainHead_v1_storage", rpc_params![&sub_id, &block_hash, items])
 		.await
 		.unwrap();
 	let operation_id = match response {
@@ -2932,7 +2906,7 @@ async fn ensure_operation_limits_works() {
 	let call_parameters = hex_string(&alice_id.encode());
 	let response: MethodResponse = api
 		.call(
-			"chainHead_unstable_call",
+			"chainHead_v1_call",
 			[&sub_id, &block_hash, "AccountNonceApi_account_nonce", &call_parameters],
 		)
 		.await
@@ -2977,7 +2951,7 @@ async fn check_continue_operation() {
 	)
 	.into_rpc();
 
-	let mut sub = api.subscribe_unbounded("chainHead_unstable_follow", [true]).await.unwrap();
+	let mut sub = api.subscribe_unbounded("chainHead_v1_follow", [true]).await.unwrap();
 	let sub_id = sub.subscription_id();
 	let sub_id = serde_json::to_string(&sub_id).unwrap();
 
@@ -3014,17 +2988,17 @@ async fn check_continue_operation() {
 
 	// Invalid subscription ID must produce no results.
 	let _res: () = api
-		.call("chainHead_unstable_continue", ["invalid_sub_id", &invalid_hash])
+		.call("chainHead_v1_continue", ["invalid_sub_id", &invalid_hash])
 		.await
 		.unwrap();
 
 	// Invalid operation ID must produce no results.
-	let _res: () = api.call("chainHead_unstable_continue", [&sub_id, &invalid_hash]).await.unwrap();
+	let _res: () = api.call("chainHead_v1_continue", [&sub_id, &invalid_hash]).await.unwrap();
 
 	// Valid call with storage at the key.
 	let response: MethodResponse = api
 		.call(
-			"chainHead_unstable_storage",
+			"chainHead_v1_storage",
 			rpc_params![
 				&sub_id,
 				&block_hash,
@@ -3060,7 +3034,7 @@ async fn check_continue_operation() {
 		std::time::Duration::from_secs(DOES_NOT_PRODUCE_EVENTS_SECONDS),
 	)
 	.await;
-	let _res: () = api.call("chainHead_unstable_continue", [&sub_id, &operation_id]).await.unwrap();
+	let _res: () = api.call("chainHead_v1_continue", [&sub_id, &operation_id]).await.unwrap();
 	assert_matches!(
 		get_next_event::<FollowEvent<String>>(&mut sub).await,
 		FollowEvent::OperationStorageItems(res) if res.operation_id == operation_id &&
@@ -3079,7 +3053,7 @@ async fn check_continue_operation() {
 		std::time::Duration::from_secs(DOES_NOT_PRODUCE_EVENTS_SECONDS),
 	)
 	.await;
-	let _res: () = api.call("chainHead_unstable_continue", [&sub_id, &operation_id]).await.unwrap();
+	let _res: () = api.call("chainHead_v1_continue", [&sub_id, &operation_id]).await.unwrap();
 	assert_matches!(
 		get_next_event::<FollowEvent<String>>(&mut sub).await,
 		FollowEvent::OperationStorageItems(res) if res.operation_id == operation_id &&
@@ -3099,7 +3073,7 @@ async fn check_continue_operation() {
 		std::time::Duration::from_secs(DOES_NOT_PRODUCE_EVENTS_SECONDS),
 	)
 	.await;
-	let _res: () = api.call("chainHead_unstable_continue", [&sub_id, &operation_id]).await.unwrap();
+	let _res: () = api.call("chainHead_v1_continue", [&sub_id, &operation_id]).await.unwrap();
 	assert_matches!(
 		get_next_event::<FollowEvent<String>>(&mut sub).await,
 		FollowEvent::OperationStorageItems(res) if res.operation_id == operation_id &&
@@ -3118,7 +3092,7 @@ async fn check_continue_operation() {
 		std::time::Duration::from_secs(DOES_NOT_PRODUCE_EVENTS_SECONDS),
 	)
 	.await;
-	let _res: () = api.call("chainHead_unstable_continue", [&sub_id, &operation_id]).await.unwrap();
+	let _res: () = api.call("chainHead_v1_continue", [&sub_id, &operation_id]).await.unwrap();
 	assert_matches!(
 		get_next_event::<FollowEvent<String>>(&mut sub).await,
 		FollowEvent::OperationStorageItems(res) if res.operation_id == operation_id &&
@@ -3162,7 +3136,7 @@ async fn stop_storage_operation() {
 	)
 	.into_rpc();
 
-	let mut sub = api.subscribe_unbounded("chainHead_unstable_follow", [true]).await.unwrap();
+	let mut sub = api.subscribe_unbounded("chainHead_v1_follow", [true]).await.unwrap();
 	let sub_id = sub.subscription_id();
 	let sub_id = serde_json::to_string(&sub_id).unwrap();
 
@@ -3196,20 +3170,17 @@ async fn stop_storage_operation() {
 
 	// Invalid subscription ID must produce no results.
 	let _res: () = api
-		.call("chainHead_unstable_stopOperation", ["invalid_sub_id", &invalid_hash])
+		.call("chainHead_v1_stopOperation", ["invalid_sub_id", &invalid_hash])
 		.await
 		.unwrap();
 
 	// Invalid operation ID must produce no results.
-	let _res: () = api
-		.call("chainHead_unstable_stopOperation", [&sub_id, &invalid_hash])
-		.await
-		.unwrap();
+	let _res: () = api.call("chainHead_v1_stopOperation", [&sub_id, &invalid_hash]).await.unwrap();
 
 	// Valid call with storage at the key.
 	let response: MethodResponse = api
 		.call(
-			"chainHead_unstable_storage",
+			"chainHead_v1_storage",
 			rpc_params![
 				&sub_id,
 				&block_hash,
@@ -3241,10 +3212,7 @@ async fn stop_storage_operation() {
 	);
 
 	// Stop the operation.
-	let _res: () = api
-		.call("chainHead_unstable_stopOperation", [&sub_id, &operation_id])
-		.await
-		.unwrap();
+	let _res: () = api.call("chainHead_v1_stopOperation", [&sub_id, &operation_id]).await.unwrap();
 
 	does_not_produce_event::<FollowEvent<String>>(
 		&mut sub,
@@ -3272,7 +3240,7 @@ async fn storage_closest_merkle_value() {
 		// Valid call with storage at the keys.
 		let response: MethodResponse = api
 			.call(
-				"chainHead_unstable_storage",
+				"chainHead_v1_storage",
 				rpc_params![
 					&sub_id,
 					&block_hash,
@@ -3466,7 +3434,7 @@ async fn chain_head_stop_all_subscriptions() {
 	)
 	.into_rpc();
 
-	let mut sub = api.subscribe_unbounded("chainHead_unstable_follow", [true]).await.unwrap();
+	let mut sub = api.subscribe_unbounded("chainHead_v1_follow", [true]).await.unwrap();
 
 	// Ensure the imported block is propagated and pinned for this subscription.
 	assert_matches!(
@@ -3500,8 +3468,7 @@ async fn chain_head_stop_all_subscriptions() {
 		);
 	}
 
-	let mut second_sub =
-		api.subscribe_unbounded("chainHead_unstable_follow", [true]).await.unwrap();
+	let mut second_sub = api.subscribe_unbounded("chainHead_v1_follow", [true]).await.unwrap();
 	// Lagging detected, the stop event is delivered immediately.
 	assert_matches!(
 		get_next_event::<FollowEvent<String>>(&mut second_sub).await,
@@ -3512,14 +3479,14 @@ async fn chain_head_stop_all_subscriptions() {
 	assert_matches!(get_next_event::<FollowEvent<String>>(&mut sub).await, FollowEvent::Stop);
 
 	// Other subscriptions cannot be started until the suspension period is over.
-	let mut sub = api.subscribe_unbounded("chainHead_unstable_follow", [true]).await.unwrap();
+	let mut sub = api.subscribe_unbounded("chainHead_v1_follow", [true]).await.unwrap();
 	// Should receive the stop event immediately.
 	assert_matches!(get_next_event::<FollowEvent<String>>(&mut sub).await, FollowEvent::Stop);
 
 	// For the next subscription, lagging distance must be smaller.
 	client.finalize_block(parent_hash, None).unwrap();
 
-	let mut sub = api.subscribe_unbounded("chainHead_unstable_follow", [true]).await.unwrap();
+	let mut sub = api.subscribe_unbounded("chainHead_v1_follow", [true]).await.unwrap();
 	assert_matches!(
 		get_next_event::<FollowEvent<String>>(&mut sub).await,
 		FollowEvent::Initialized(_)
@@ -3681,12 +3648,12 @@ async fn chain_head_limit_reached() {
 	)
 	.into_rpc();
 
-	let mut sub = api.subscribe_unbounded("chainHead_unstable_follow", [true]).await.unwrap();
+	let mut sub = api.subscribe_unbounded("chainHead_v1_follow", [true]).await.unwrap();
 
 	// Initialized must always be reported first.
 	let _event: FollowEvent<String> = get_next_event(&mut sub).await;
 
-	let error = api.subscribe_unbounded("chainHead_unstable_follow", [true]).await.unwrap_err();
+	let error = api.subscribe_unbounded("chainHead_v1_follow", [true]).await.unwrap_err();
 	assert!(error
 		.to_string()
 		.contains("Maximum number of chainHead_follow has been reached"));
@@ -3696,7 +3663,7 @@ async fn chain_head_limit_reached() {
 	// Ensure the `chainHead_unfollow` is propagated to the server.
 	tokio::time::sleep(std::time::Duration::from_secs(5)).await;
 
-	let mut sub = api.subscribe_unbounded("chainHead_unstable_follow", [true]).await.unwrap();
+	let mut sub = api.subscribe_unbounded("chainHead_v1_follow", [true]).await.unwrap();
 	// Initialized must always be reported first.
 	let _event: FollowEvent<String> = get_next_event(&mut sub).await;
 }
@@ -3723,7 +3690,7 @@ async fn follow_unique_pruned_blocks() {
 	.into_rpc();
 
 	let finalized_hash = client.info().finalized_hash;
-	let mut sub = api.subscribe_unbounded("chainHead_unstable_follow", [false]).await.unwrap();
+	let mut sub = api.subscribe_unbounded("chainHead_v1_follow", [false]).await.unwrap();
 
 	// Initialized must always be reported first.
 	let event: FollowEvent<String> = get_next_event(&mut sub).await;
@@ -3827,7 +3794,7 @@ async fn follow_unique_pruned_blocks() {
 	let sub_id = sub.subscription_id();
 	let sub_id = serde_json::to_string(&sub_id).unwrap();
 	let hash = format!("{:?}", block_2_hash);
-	let _res: () = api.call("chainHead_unstable_unpin", rpc_params![&sub_id, &hash]).await.unwrap();
+	let _res: () = api.call("chainHead_v1_unpin", rpc_params![&sub_id, &hash]).await.unwrap();
 
 	// Import block 7 and check it.
 	let block_7_hash = import_block(client.clone(), block_6_hash, 3).await.hash();