diff --git a/src/commands/collective.rs b/src/commands/collective.rs
index 80da16a0b7210ad8509a46da5cdded92f002b0b7..9717d66537e0639317f50fd28f9b5324bbf90a90 100644
--- a/src/commands/collective.rs
+++ b/src/commands/collective.rs
@@ -8,6 +8,8 @@ pub enum Subcommand {
 	#[default]
 	/// List members of the technical committee
 	Members,
+	/// Propose a hex encoded call
+	Propose { hex: String },
 	/// List proposals to the technical committee
 	Proposals,
 	/// Vote a proposal to the technical committee
@@ -23,24 +25,18 @@ pub enum Subcommand {
 
 /// handle technical committee commands
 pub async fn handle_command(data: Data, command: Subcommand) -> Result<(), GcliError> {
-	let mut data = data.build_client().await?.build_indexer().await?;
+	let data = data.build_client().await?;
 	match command {
-		Subcommand::Members => {
-			data = data.build_client().await?;
-			commands::collective::technical_committee_members(&data).await?
-		}
-		Subcommand::Proposals => {
-			data = data.build_client().await?;
-			commands::collective::technical_committee_proposals(data.client()).await?
-		}
+		Subcommand::Members => technical_committee_members(&data).await?,
+		Subcommand::Propose { hex } => technical_committee_propose(&data, &hex).await?,
+		Subcommand::Proposals => technical_committee_proposals(data.client()).await?,
 		Subcommand::Vote { hash, index, vote } => {
-			data = data.build_client().await?;
 			let vote = match vote {
 				0 => false,
 				1 => true,
 				_ => panic!("Vote must be written 0 if you disagree, or 1 if you agree."),
 			};
-			commands::collective::technical_committee_vote(
+			technical_committee_vote(
 				&data, hash, //Hash::from_str(&hash).expect("Invalid hash formatting"),
 				index, vote,
 			)
@@ -141,3 +137,17 @@ pub async fn technical_committee_vote(
 	)
 	.await
 }
+
+/// propose call given as hexadecimal
+/// can be generated with `subxt explore` for example
+pub async fn technical_committee_propose(data: &Data, proposal: &str) -> Result<(), subxt::Error> {
+	let raw_call = hex::decode(proposal).expect("invalid hex");
+	let call = codec::decode_from_bytes(raw_call.into()).expect("invalid call");
+	let payload = runtime::tx().technical_committee().propose(5, call, 100);
+
+	submit_call_and_look_event::<
+		runtime::technical_committee::events::Proposed,
+		Payload<runtime::technical_committee::calls::types::Propose>,
+	>(data, &payload)
+	.await
+}
diff --git a/src/display.rs b/src/display.rs
index 7f68d8d97b3c7cdc3ffa19ee88f72d59adce27b7..3320ab4391eff8de3f50b5cde7e96b99e8d163bf 100644
--- a/src/display.rs
+++ b/src/display.rs
@@ -150,3 +150,8 @@ impl DisplayEvent for runtime::sudo::events::Sudid {
 		format!("SUDO call succeeded {:?}", self)
 	}
 }
+impl DisplayEvent for runtime::technical_committee::events::Proposed {
+	fn display(&self, _data: &Data) -> String {
+		format!("proposed {:?}", self)
+	}
+}