diff --git a/components/chainhook-cli/src/config/mod.rs b/components/chainhook-cli/src/config/mod.rs
index cba31806e..80ad2215b 100644
--- a/components/chainhook-cli/src/config/mod.rs
+++ b/components/chainhook-cli/src/config/mod.rs
@@ -357,7 +357,7 @@ impl Config {
             (false, true, false, _) => Config::testnet_default(),
             (false, false, true, _) => Config::mainnet_default(),
             (false, false, false, Some(config_path)) => Config::from_file_path(config_path)?,
-            _ => Err("Invalid combination of arguments".to_string())?,
+            _ => Err("Must include environment flag (for example --devnet)".to_string())?,
         };
         Ok(config)
     }
diff --git a/components/chainhook-cli/src/config/tests/mod.rs b/components/chainhook-cli/src/config/tests/mod.rs
index fa48fe655..c63eb2a13 100644
--- a/components/chainhook-cli/src/config/tests/mod.rs
+++ b/components/chainhook-cli/src/config/tests/mod.rs
@@ -178,5 +178,5 @@ fn it_has_default_config_for_each_network() {
     let config = Config::default(false, false, false, &Some(path)).unwrap();
     assert_eq!(config.network.bitcoin_network, BitcoinNetwork::Regtest);
     assert_eq!(config.network.stacks_network, StacksNetwork::Devnet);
-    Config::default(true, true, false, &None).expect_err("expected invalid combination error");
+    Config::default(true, true, false, &None).expect_err("expected missing environment flag error");
 }
diff --git a/components/chainhook-cli/src/service/tests/helpers/mock_stacks_node.rs b/components/chainhook-cli/src/service/tests/helpers/mock_stacks_node.rs
index 883853689..c07b65475 100644
--- a/components/chainhook-cli/src/service/tests/helpers/mock_stacks_node.rs
+++ b/components/chainhook-cli/src/service/tests/helpers/mock_stacks_node.rs
@@ -1,7 +1,9 @@
 use crate::scan::stacks::{Record, RecordKind};
 use crate::service::tests::helpers::mock_bitcoin_rpc::TipData;
 use chainhook_sdk::indexer::bitcoin::NewBitcoinBlock;
-use chainhook_sdk::indexer::stacks::{NewBlock, NewEvent, NewTransaction, RewardSet, RewardSetSigner};
+use chainhook_sdk::indexer::stacks::{
+    NewBlock, NewEvent, NewTransaction, RewardSet, RewardSetSigner,
+};
 use chainhook_sdk::types::{
     FTBurnEventData, FTMintEventData, FTTransferEventData, NFTBurnEventData, NFTMintEventData,
     NFTTransferEventData, STXBurnEventData, STXLockEventData, STXMintEventData,
@@ -87,6 +89,13 @@ fn create_stacks_new_event(
     } else {
         None
     };
+    let tenure_change_event = if let StacksTransactionEventPayload::TenureChangeEvent(data) = &event
+    {
+        event_type = "tenure_change_event".to_string();
+        Some(serde_json::to_value(data).unwrap())
+    } else {
+        None
+    };
     let contract_event = if let StacksTransactionEventPayload::SmartContractEvent(data) = &event {
         event_type = "smart_contract_print_event".to_string();
         Some(serde_json::to_value(data).unwrap())
@@ -112,6 +121,7 @@ fn create_stacks_new_event(
         data_map_insert_event: None,
         data_map_update_event: None,
         data_map_delete_event: None,
+        tenure_change_event,
         contract_event,
     }
 }
@@ -336,12 +346,7 @@ pub async fn mine_stacks_block(
         .map_err(|e| format!("failed to send new_block request: {}", e))?
         .text()
         .await
-        .map_err(|e| {
-            format!(
-                "failed to parse response for new_block request: {}",
-                e
-            )
-        })?;
+        .map_err(|e| format!("failed to parse response for new_block request: {}", e))?;
     Ok(())
 }
 
@@ -414,12 +419,7 @@ async fn call_new_burn_block(
         .map_err(|e| format!("failed to send new_burn_block request: {}", e))?
         .text()
         .await
-        .map_err(|e| {
-            format!(
-                "failed to parse response for new_burn_block request: {}",
-                e
-            )
-        })?;
+        .map_err(|e| format!("failed to parse response for new_burn_block request: {}", e))?;
     Ok(())
 }
 
diff --git a/components/chainhook-sdk/src/chainhooks/stacks/mod.rs b/components/chainhook-sdk/src/chainhooks/stacks/mod.rs
index 1b8e5bfa9..9b402722c 100644
--- a/components/chainhook-sdk/src/chainhooks/stacks/mod.rs
+++ b/components/chainhook-sdk/src/chainhooks/stacks/mod.rs
@@ -1,11 +1,11 @@
 use crate::observer::EventObserverConfig;
 use crate::utils::{AbstractStacksBlock, Context, MAX_BLOCK_HEIGHTS_ENTRIES};
 
+use super::types::validate_txid;
 use super::types::{
     append_error_context, BlockIdentifierIndexRule, ChainhookInstance, ExactMatchingRule,
     HookAction,
 };
-use super::types::validate_txid;
 use chainhook_types::{
     BlockIdentifier, StacksChainEvent, StacksNetwork, StacksTransactionData,
     StacksTransactionEvent, StacksTransactionEventPayload, StacksTransactionKind,
@@ -258,6 +258,7 @@ pub enum StacksPredicate {
     FtEvent(StacksFtEventBasedPredicate),
     NftEvent(StacksNftEventBasedPredicate),
     StxEvent(StacksStxEventBasedPredicate),
+    TenureChange(TenureChangeBasedPredicate),
     Txid(ExactMatchingRule),
 }
 
@@ -299,6 +300,7 @@ impl StacksPredicate {
             StacksPredicate::FtEvent(_) => {}
             StacksPredicate::NftEvent(_) => {}
             StacksPredicate::StxEvent(_) => {}
+            StacksPredicate::TenureChange(_) => {}
             StacksPredicate::Txid(ExactMatchingRule::Equals(txid)) => {
                 if let Err(e) = validate_txid(txid) {
                     return Err(append_error_context(
@@ -452,6 +454,12 @@ pub struct StacksStxEventBasedPredicate {
     pub actions: Vec<String>,
 }
 
+#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, JsonSchema)]
+#[serde(rename_all = "snake_case")]
+pub struct TenureChangeBasedPredicate {
+    pub actions: Vec<String>,
+}
+
 #[derive(Clone)]
 pub struct StacksTriggerChainhook<'a> {
     pub chainhook: &'a StacksChainhookInstance,
@@ -484,17 +492,13 @@ pub struct StacksChainhookOccurrencePayload {
 }
 
 impl StacksChainhookOccurrencePayload {
-    pub fn from_trigger(
-        trigger: StacksTriggerChainhook<'_>,
-    ) -> StacksChainhookOccurrencePayload {
+    pub fn from_trigger(trigger: StacksTriggerChainhook<'_>) -> StacksChainhookOccurrencePayload {
         StacksChainhookOccurrencePayload {
             apply: trigger
                 .apply
                 .into_iter()
                 .map(|(transactions, block)| {
-                    let transactions = transactions
-                        .into_iter().cloned()
-                        .collect::<Vec<_>>();
+                    let transactions = transactions.into_iter().cloned().collect::<Vec<_>>();
                     StacksApplyTransactionPayload {
                         block_identifier: block.get_identifier().clone(),
                         transactions,
@@ -505,9 +509,7 @@ impl StacksChainhookOccurrencePayload {
                 .rollback
                 .into_iter()
                 .map(|(transactions, block)| {
-                    let transactions = transactions
-                        .into_iter().cloned()
-                        .collect::<Vec<_>>();
+                    let transactions = transactions.into_iter().cloned().collect::<Vec<_>>();
                     StacksRollbackTransactionPayload {
                         block_identifier: block.get_identifier().clone(),
                         transactions,
@@ -790,6 +792,7 @@ pub fn evaluate_stacks_predicate_on_block<'a>(
         | StacksPredicate::FtEvent(_)
         | StacksPredicate::NftEvent(_)
         | StacksPredicate::StxEvent(_)
+        | StacksPredicate::TenureChange(_)
         | StacksPredicate::PrintEvent(_)
         | StacksPredicate::Txid(_) => unreachable!(),
     }
@@ -816,7 +819,7 @@ pub fn evaluate_stacks_predicate_on_transaction<'a>(
             _ => false,
         },
         StacksPredicate::ContractDeployment(StacksContractDeploymentPredicate::ImplementTrait(
-            stacks_trait,
+            _stacks_trait,
         )) => match &transaction.metadata.kind {
             StacksTransactionKind::ContractDeployment(_actual_deployment) => {
                 ctx.try_log(|logger| {
@@ -922,6 +925,18 @@ pub fn evaluate_stacks_predicate_on_transaction<'a>(
             }
             false
         }
+        StacksPredicate::TenureChange(_expected_event) => {
+            for event in transaction.metadata.receipt.events.iter() {
+                if matches!(
+                    &event.event_payload,
+                    StacksTransactionEventPayload::TenureChangeEvent(_)
+                ) {
+                    return true;
+                }
+            }
+            false
+        }
+
         StacksPredicate::StxEvent(expected_event) => {
             let expecting_mint = expected_event.actions.contains(&"mint".to_string());
             let expecting_transfer = expected_event.actions.contains(&"transfer".to_string());
@@ -949,7 +964,9 @@ pub fn evaluate_stacks_predicate_on_transaction<'a>(
         }
         StacksPredicate::PrintEvent(expected_event) => {
             for event in transaction.metadata.receipt.events.iter() {
-                if let StacksTransactionEventPayload::SmartContractEvent(actual) = &event.event_payload {
+                if let StacksTransactionEventPayload::SmartContractEvent(actual) =
+                    &event.event_payload
+                {
                     if actual.topic == "print" {
                         match expected_event {
                             StacksPrintEventBasedPredicate::Contains {
@@ -1212,6 +1229,17 @@ pub fn serialized_event_with_decoded_clarity_value(
                 "position": event.position
             })
         }
+        StacksTransactionEventPayload::TenureChangeEvent(_payload) => {
+            json!({
+                "type": "TenureChangeEvent",
+                // "data": {
+                //     "contract_identifier": payload.contract_identifier,
+                //     "topic": payload.topic,
+                //     "value": serialized_decoded_clarity_value(&payload.tenure_change, ctx),
+                // },
+                "position": event.position
+            })
+        }
     }
 }
 
@@ -1235,7 +1263,7 @@ pub fn serialized_decoded_clarity_value(hex_value: &str, ctx: &Context) -> serde
         Ok(bytes) => bytes,
         _ => return json!(hex_value.to_string()),
     };
-    
+
     match ClarityValue::consensus_deserialize(&mut Cursor::new(&value_bytes)) {
         Ok(value) => serialize_to_json(&value),
         Err(e) => {
diff --git a/components/chainhook-sdk/src/indexer/stacks/mod.rs b/components/chainhook-sdk/src/indexer/stacks/mod.rs
index 2e54a7992..5237311f1 100644
--- a/components/chainhook-sdk/src/indexer/stacks/mod.rs
+++ b/components/chainhook-sdk/src/indexer/stacks/mod.rs
@@ -41,10 +41,10 @@ pub struct NewBlock {
 
     #[serde(skip_serializing_if = "Option::is_none")]
     pub block_time: Option<u64>,
-    
+
     #[serde(skip_serializing_if = "Option::is_none")]
     pub signer_bitvec: Option<String>,
-    
+
     #[serde(skip_serializing_if = "Option::is_none")]
     pub signer_signature: Option<Vec<String>>,
 
@@ -147,6 +147,7 @@ pub struct NewEvent {
     pub data_map_insert_event: Option<JsonValue>,
     pub data_map_update_event: Option<JsonValue>,
     pub data_map_delete_event: Option<JsonValue>,
+    pub tenure_change_event: Option<JsonValue>,
     pub contract_event: Option<JsonValue>,
 }
 
@@ -278,6 +279,15 @@ impl NewEvent {
                     index: self.event_index,
                 },
             });
+        } else if let Some(ref event_data) = self.tenure_change_event {
+            let data: TenureChangeEventData =
+                serde_json::from_value(event_data.clone()).expect("Unable to decode event_data");
+            return Ok(StacksTransactionEvent {
+                event_payload: StacksTransactionEventPayload::TenureChangeEvent(data.clone()),
+                position: StacksTransactionEventPosition {
+                    index: self.event_index,
+                },
+            });
         } else if let Some(ref event_data) = self.contract_event {
             let data: SmartContractEventData =
                 serde_json::from_value(event_data.clone()).expect("Unable to decode event_data");
@@ -387,8 +397,7 @@ pub fn standardize_stacks_block(
                     }
                     return Err(format!(
                         "unable to standardize block #{} ({})",
-                        block.block_height,
-                        e
+                        block.block_height, e
                     ));
                 }
             };
@@ -629,7 +638,6 @@ pub fn get_value_description(raw_value: &str, ctx: &Context) -> String {
         _ => return raw_value.to_string(),
     };
 
-    
     match ClarityValue::consensus_deserialize(&mut Cursor::new(&value_bytes)) {
         Ok(value) => format!("{}", value),
         Err(e) => {
@@ -703,12 +711,12 @@ pub fn get_tx_description(
                         if let ClarityValue::Tuple(outter) = *data.data {
                             if let Some(ClarityValue::Tuple(inner)) = outter.data_map.get("data") {
                                 if let (
-                                        Some(ClarityValue::Principal(stacking_address)),
-                                        Some(ClarityValue::UInt(amount_ustx)),
-                                        Some(ClarityValue::Principal(delegate)),
-                                        Some(ClarityValue::Optional(pox_addr)),
-                                        Some(ClarityValue::Optional(unlock_burn_height)),
-                                    ) = (
+                                    Some(ClarityValue::Principal(stacking_address)),
+                                    Some(ClarityValue::UInt(amount_ustx)),
+                                    Some(ClarityValue::Principal(delegate)),
+                                    Some(ClarityValue::Optional(pox_addr)),
+                                    Some(ClarityValue::Optional(unlock_burn_height)),
+                                ) = (
                                     &outter.data_map.get("stacker"),
                                     &inner.data_map.get("amount-ustx"),
                                     &inner.data_map.get("delegate-to"),
@@ -728,17 +736,13 @@ pub fn get_tx_description(
                                                 Some(value) => match &**value {
                                                     ClarityValue::Tuple(address_comps) => {
                                                         match (
-                                                            &address_comps
-                                                                .data_map
-                                                                .get("version"),
+                                                            &address_comps.data_map.get("version"),
                                                             &address_comps
                                                                 .data_map
                                                                 .get("hashbytes"),
                                                         ) {
                                                             (
-                                                                Some(ClarityValue::UInt(
-                                                                    _version,
-                                                                )),
+                                                                Some(ClarityValue::UInt(_version)),
                                                                 Some(ClarityValue::Sequence(
                                                                     SequenceData::Buffer(
                                                                         _hashbytes,
@@ -763,14 +767,7 @@ pub fn get_tx_description(
                                             },
                                         }),
                                     );
-                                    return Ok((
-                                        description,
-                                        tx_type,
-                                        0,
-                                        0,
-                                        "".to_string(),
-                                        None,
-                                    ));
+                                    return Ok((description, tx_type, 0, 0, "".to_string(), None));
                                 }
                             }
                         }
@@ -1324,6 +1321,7 @@ pub fn get_standardized_stacks_receipt(
                 StacksTransactionEventPayload::DataMapInsertEvent(_data) => {}
                 StacksTransactionEventPayload::DataMapUpdateEvent(_data) => {}
                 StacksTransactionEventPayload::DataMapDeleteEvent(_data) => {}
+                StacksTransactionEventPayload::TenureChangeEvent(_data) => {}
                 StacksTransactionEventPayload::SmartContractEvent(data) => {
                     mutated_contracts_radius.insert(data.contract_identifier.clone());
                 }
diff --git a/components/chainhook-sdk/src/indexer/stacks/tests.rs b/components/chainhook-sdk/src/indexer/stacks/tests.rs
index 51d4d2a5c..849e08f5c 100644
--- a/components/chainhook-sdk/src/indexer/stacks/tests.rs
+++ b/components/chainhook-sdk/src/indexer/stacks/tests.rs
@@ -3,6 +3,7 @@ use chainhook_types::{
     FTBurnEventData, FTMintEventData, FTTransferEventData, NFTBurnEventData, NFTMintEventData,
     NFTTransferEventData, STXBurnEventData, STXLockEventData, STXMintEventData,
     STXTransferEventData, SmartContractEventData, StacksTransactionEventPayload,
+    TenureChangeEventData,
 };
 
 use crate::indexer::tests::helpers::stacks_events::create_new_event_from_stacks_event;
@@ -213,10 +214,10 @@ fn test_stacks_vector_040() {
     process_stacks_blocks_and_check_expectations((helpers::stacks_shapes::get_vector_040(), None));
 }
 
-// #[test]
-// fn test_stacks_vector_041() {
-//     process_stacks_blocks_and_check_expectations((helpers::shapes::get_vector_041(), None));
-// }
+#[test]
+fn test_stacks_vector_041() {
+    process_stacks_blocks_and_check_expectations((helpers::stacks_shapes::get_vector_041(), None));
+}
 
 #[test]
 fn test_stacks_vector_042() {
@@ -363,6 +364,15 @@ fn test_stacks_vector_055() {
     topic: "print".to_string(),
     hex_value: String::new(),
 }); "smart_contract_print_event")]
+#[test_case(StacksTransactionEventPayload::TenureChangeEvent(TenureChangeEventData {
+    tenure_consensus_hash: String::new(),
+    prev_tenure_consensus_hash: String::new(),
+    burn_view_consensus_hash: String::new(),
+    previous_tenure_end: String::new(),
+    previous_tenure_blocks: "9".to_string(),
+    cause: "block_found".to_string(),
+    pubkey_hash: String::new(),
+}); "tenure_change_event")]
 fn new_events_can_be_converted_into_chainhook_event(original_event: StacksTransactionEventPayload) {
     let new_event = create_new_event_from_stacks_event(original_event.clone());
     let event = new_event.into_chainhook_event().unwrap();
@@ -392,6 +402,7 @@ fn into_chainhook_event_rejects_invalid_missing_event() {
         data_map_insert_event: None,
         data_map_update_event: None,
         data_map_delete_event: None,
+        tenure_change_event: None,
         contract_event: None,
     };
     new_event
diff --git a/components/chainhook-sdk/src/indexer/tests/helpers/stacks_events.rs b/components/chainhook-sdk/src/indexer/tests/helpers/stacks_events.rs
index 4ca977bc5..cc9ede7c2 100644
--- a/components/chainhook-sdk/src/indexer/tests/helpers/stacks_events.rs
+++ b/components/chainhook-sdk/src/indexer/tests/helpers/stacks_events.rs
@@ -91,6 +91,13 @@ pub fn create_new_event_from_stacks_event(event: StacksTransactionEventPayload)
         } else {
             None
         };
+    let tenure_change_event = if let StacksTransactionEventPayload::TenureChangeEvent(data) = &event
+    {
+        event_type = "tenure_change".to_string();
+        Some(serde_json::to_value(data).unwrap())
+    } else {
+        None
+    };
     let contract_event = if let StacksTransactionEventPayload::SmartContractEvent(data) = &event {
         event_type = "smart_contract_print_event".to_string();
         Some(serde_json::to_value(data).unwrap())
@@ -116,6 +123,7 @@ pub fn create_new_event_from_stacks_event(event: StacksTransactionEventPayload)
         data_map_insert_event,
         data_map_update_event,
         data_map_delete_event,
+        tenure_change_event,
         contract_event,
     }
 }
diff --git a/components/chainhook-types-rs/src/events.rs b/components/chainhook-types-rs/src/events.rs
index 02179b51b..a9a4b6e88 100644
--- a/components/chainhook-types-rs/src/events.rs
+++ b/components/chainhook-types-rs/src/events.rs
@@ -121,6 +121,17 @@ pub struct SmartContractEventData {
     pub hex_value: String,
 }
 
+#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
+pub struct TenureChangeEventData {
+    pub tenure_consensus_hash: String,
+    pub prev_tenure_consensus_hash: String,
+    pub burn_view_consensus_hash: String,
+    pub previous_tenure_end: String,
+    pub previous_tenure_blocks: String,
+    pub cause: String,
+    pub pubkey_hash: String,
+}
+
 #[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
 #[serde(tag = "type", content = "data")]
 pub enum StacksTransactionEventPayload {
@@ -139,6 +150,7 @@ pub enum StacksTransactionEventPayload {
     DataMapUpdateEvent(DataMapUpdateEventData),
     DataMapDeleteEvent(DataMapDeleteEventData),
     SmartContractEvent(SmartContractEventData),
+    TenureChangeEvent(TenureChangeEventData),
 }
 
 #[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
diff --git a/docs/chainhook-openapi.json b/docs/chainhook-openapi.json
index 989f3c230..7d535bfe8 100644
--- a/docs/chainhook-openapi.json
+++ b/docs/chainhook-openapi.json
@@ -1180,6 +1180,27 @@
               }
             }
           },
+          {
+            "type": "object",
+            "required": [
+              "actions",
+              "scope"
+            ],
+            "properties": {
+              "scope": {
+                "type": "string",
+                "enum": [
+                  "tenure_change"
+                ]
+              },
+              "actions": {
+                "type": "array",
+                "items": {
+                  "type": "string"
+                }
+              }
+            }
+          },
           {
             "type": "object",
             "oneOf": [