Bug: [FEATURE] Add resume api to resume/continue the tasks in process tracker for revenue_recovery



Add resume api to resume/continue the tasks in process tracker for revenue_recovery. This PR enables us to resume the EXECUTE and CALCULATE that are stuck in an intermediate state .

diff --git a/crates/api_models/src/events/revenue_recovery.rs b/crates/api_models/src/events/revenue_recovery.rs
index 8327dfdded2..ee6bf464d1f 100644
--- a/crates/api_models/src/events/revenue_recovery.rs
+++ b/crates/api_models/src/events/revenue_recovery.rs
@@ -1,6 +1,8 @@
 use common_utils::events::{ApiEventMetric, ApiEventsType};
 
-use crate::process_tracker::revenue_recovery::{RevenueRecoveryId, RevenueRecoveryResponse};
+use crate::process_tracker::revenue_recovery::{
+    RevenueRecoveryId, RevenueRecoveryResponse, RevenueRecoveryRetriggerRequest,
+};
 
 impl ApiEventMetric for RevenueRecoveryResponse {
     fn get_api_event_type(&self) -> Option<ApiEventsType> {
@@ -12,3 +14,8 @@ impl ApiEventMetric for RevenueRecoveryId {
         Some(ApiEventsType::ProcessTracker)
     }
 }
+impl ApiEventMetric for RevenueRecoveryRetriggerRequest {
+    fn get_api_event_type(&self) -> Option<ApiEventsType> {
+        Some(ApiEventsType::ProcessTracker)
+    }
+}
diff --git a/crates/api_models/src/process_tracker/revenue_recovery.rs b/crates/api_models/src/process_tracker/revenue_recovery.rs
index 7baeb547a68..131766e72d5 100644
--- a/crates/api_models/src/process_tracker/revenue_recovery.rs
+++ b/crates/api_models/src/process_tracker/revenue_recovery.rs
@@ -8,7 +8,11 @@ use crate::enums;
 pub struct RevenueRecoveryResponse {
     pub id: String,
     pub name: Option<String>,
+    #[schema(example = "2022-09-10T10:11:12Z")]
+    #[serde(default, with = "common_utils::custom_serde::iso8601::option")]
     pub schedule_time_for_payment: Option<PrimitiveDateTime>,
+    #[schema(example = "2022-09-10T10:11:12Z")]
+    #[serde(default, with = "common_utils::custom_serde::iso8601::option")]
     pub schedule_time_for_psync: Option<PrimitiveDateTime>,
     #[schema(value_type = ProcessTrackerStatus, example = "finish")]
     pub status: enums::ProcessTrackerStatus,
@@ -19,3 +23,17 @@ pub struct RevenueRecoveryResponse {
 pub struct RevenueRecoveryId {
     pub revenue_recovery_id: id_type::GlobalPaymentId,
 }
+
+#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
+pub struct RevenueRecoveryRetriggerRequest {
+    /// The task we want to resume
+    pub revenue_recovery_task: String,
+    /// Time at which the job was scheduled at
+    #[schema(example = "2022-09-10T10:11:12Z")]
+    #[serde(default, with = "common_utils::custom_serde::iso8601::option")]
+    pub schedule_time: Option<PrimitiveDateTime>,
+    /// Status of The Process Tracker Task
+    pub status: enums::ProcessTrackerStatus,
+    /// Business Status of The Process Tracker Task
+    pub business_status: String,
+}
diff --git a/crates/router/src/core/revenue_recovery.rs b/crates/router/src/core/revenue_recovery.rs
index f9662a745dd..dee8a6ee9ff 100644
--- a/crates/router/src/core/revenue_recovery.rs
+++ b/crates/router/src/core/revenue_recovery.rs
@@ -5,7 +5,7 @@ use std::marker::PhantomData;
 
 use api_models::{
     enums,
-    payments::{self as api_payments, PaymentsResponse},
+    payments::{self as api_payments, PaymentsGetIntentRequest, PaymentsResponse},
     process_tracker::revenue_recovery,
     webhooks,
 };
@@ -16,10 +16,10 @@ use common_utils::{
     id_type,
 };
 use diesel_models::{enums as diesel_enum, process_tracker::business_status};
-use error_stack::{self, ResultExt};
+use error_stack::{self, report, ResultExt};
 use hyperswitch_domain_models::{
     merchant_context,
-    payments::{PaymentIntent, PaymentStatusData},
+    payments::{PaymentIntent, PaymentIntentData, PaymentStatusData},
     revenue_recovery as domain_revenue_recovery, ApiModelToDieselModelConvertor,
 };
 use scheduler::errors as sch_errors;
@@ -43,12 +43,11 @@ use crate::{
         storage::{self, revenue_recovery as pcr},
         transformers::{ForeignFrom, ForeignInto},
     },
-    workflows,
+    workflows::revenue_recovery as revenue_recovery_workflow,
 };
-
-pub const EXECUTE_WORKFLOW: &str = "EXECUTE_WORKFLOW";
-pub const PSYNC_WORKFLOW: &str = "PSYNC_WORKFLOW";
 pub const CALCULATE_WORKFLOW: &str = "CALCULATE_WORKFLOW";
+pub const PSYNC_WORKFLOW: &str = "PSYNC_WORKFLOW";
+pub const EXECUTE_WORKFLOW: &str = "EXECUTE_WORKFLOW";
 
 #[allow(clippy::too_many_arguments)]
 pub async fn upsert_calculate_pcr_task(
@@ -530,25 +529,26 @@ pub async fn perform_calculate_workflow(
     .await?;
 
     // 2. Get best available token
-    let best_time_to_schedule = match workflows::revenue_recovery::get_token_with_schedule_time_based_on_retry_algorithm_type(
-        state,
-        &connector_customer_id,
-        payment_intent,
-        retry_algorithm_type,
-        process.retry_count,
-    )
-    .await
-    {
-        Ok(token_opt) => token_opt,
-        Err(e) => {
-            logger::error!(
-                error = ?e,
-                connector_customer_id = %connector_customer_id,
-                "Failed to get best PSP token"
-            );
-            None
-        }
-    };
+    let best_time_to_schedule =
+        match revenue_recovery_workflow::get_token_with_schedule_time_based_on_retry_algorithm_type(
+            state,
+            &connector_customer_id,
+            payment_intent,
+            retry_algorithm_type,
+            process.retry_count,
+        )
+        .await
+        {
+            Ok(token_opt) => token_opt,
+            Err(e) => {
+                logger::error!(
+                    error = ?e,
+                    connector_customer_id = %connector_customer_id,
+                    "Failed to get best PSP token"
+                );
+                None
+            }
+        };
 
     match best_time_to_schedule {
         Some(scheduled_time) => {
@@ -1017,6 +1017,116 @@ pub async fn retrieve_revenue_recovery_process_tracker(
     Ok(ApplicationResponse::Json(response))
 }
 
+pub async fn resume_revenue_recovery_process_tracker(
+    state: SessionState,
+    id: id_type::GlobalPaymentId,
+    request_retrigger: revenue_recovery::RevenueRecoveryRetriggerRequest,
+) -> RouterResponse<revenue_recovery::RevenueRecoveryResponse> {
+    let db = &*state.store;
+    let task = request_retrigger.revenue_recovery_task;
+    let runner = storage::ProcessTrackerRunner::PassiveRecoveryWorkflow;
+    let process_tracker_id = id.get_execute_revenue_recovery_id(&task, runner);
+
+    let process_tracker = db
+        .find_process_by_id(&process_tracker_id)
+        .await
+        .to_not_found_response(errors::ApiErrorResponse::ResourceIdNotFound)
+        .attach_printable("error retrieving the process tracker id")?
+        .get_required_value("Process Tracker")
+        .change_context(errors::ApiErrorResponse::GenericNotFoundError {
+            message: "Entry For the following id doesn't exists".to_owned(),
+        })?;
+
+    let tracking_data = process_tracker
+        .tracking_data
+        .clone()
+        .parse_value::<pcr::RevenueRecoveryWorkflowTrackingData>("PCRWorkflowTrackingData")
+        .change_context(errors::ApiErrorResponse::InternalServerError)
+        .attach_printable("unable to deserialize  Pcr Workflow Tracking Data")?;
+
+    //Call payment intent to check the status
+    let request = PaymentsGetIntentRequest { id: id.clone() };
+    let revenue_recovery_payment_data =
+        revenue_recovery_workflow::extract_data_and_perform_action(&state, &tracking_data)
+            .await
+            .change_context(errors::ApiErrorResponse::GenericNotFoundError {
+                message: "Failed to extract the revenue recovery data".to_owned(),
+            })?;
+    let merchant_context_from_revenue_recovery_payment_data =
+        domain::MerchantContext::NormalMerchant(Box::new(domain::Context(
+            revenue_recovery_payment_data.merchant_account.clone(),
+            revenue_recovery_payment_data.key_store.clone(),
+        )));
+    let create_intent_response = payments::payments_intent_core::<
+        router_api_types::PaymentGetIntent,
+        router_api_types::payments::PaymentsIntentResponse,
+        _,
+        _,
+        PaymentIntentData<router_api_types::PaymentGetIntent>,
+    >(
+        state.clone(),
+        state.get_req_state(),
+        merchant_context_from_revenue_recovery_payment_data,
+        revenue_recovery_payment_data.profile.clone(),
+        payments::operations::PaymentGetIntent,
+        request,
+        tracking_data.global_payment_id.clone(),
+        hyperswitch_domain_models::payments::HeaderPayload::default(),
+    )
+    .await?;
+
+    let response = create_intent_response
+        .get_json_body()
+        .change_context(errors::ApiErrorResponse::InternalServerError)
+        .attach_printable("Unexpected response from payments core")?;
+
+    match response.status {
+        enums::IntentStatus::Failed => {
+            let pt_update = storage::ProcessTrackerUpdate::Update {
+                name: process_tracker.name.clone(),
+                tracking_data: Some(process_tracker.tracking_data.clone()),
+                business_status: Some(request_retrigger.business_status.clone()),
+                status: Some(request_retrigger.status),
+                updated_at: Some(common_utils::date_time::now()),
+                retry_count: Some(process_tracker.retry_count + 1),
+                schedule_time: Some(request_retrigger.schedule_time.unwrap_or(
+                    common_utils::date_time::now().saturating_add(time::Duration::seconds(600)),
+                )),
+            };
+            let updated_pt = db
+                .update_process(process_tracker, pt_update)
+                .await
+                .change_context(errors::ApiErrorResponse::GenericNotFoundError {
+                    message: "Failed to update the process tracker".to_owned(),
+                })?;
+            let response = revenue_recovery::RevenueRecoveryResponse {
+                id: updated_pt.id,
+                name: updated_pt.name,
+                schedule_time_for_payment: updated_pt.schedule_time,
+                schedule_time_for_psync: None,
+                status: updated_pt.status,
+                business_status: updated_pt.business_status,
+            };
+            Ok(ApplicationResponse::Json(response))
+        }
+        enums::IntentStatus::Succeeded
+        | enums::IntentStatus::Cancelled
+        | enums::IntentStatus::CancelledPostCapture
+        | enums::IntentStatus::Processing
+        | enums::IntentStatus::RequiresCustomerAction
+        | enums::IntentStatus::RequiresMerchantAction
+        | enums::IntentStatus::RequiresPaymentMethod
+        | enums::IntentStatus::RequiresConfirmation
+        | enums::IntentStatus::RequiresCapture
+        | enums::IntentStatus::PartiallyCaptured
+        | enums::IntentStatus::PartiallyCapturedAndCapturable
+        | enums::IntentStatus::PartiallyAuthorizedAndRequiresCapture
+        | enums::IntentStatus::Conflicted
+        | enums::IntentStatus::Expired => Err(report!(errors::ApiErrorResponse::NotSupported {
+            message: "Invalid Payment Status ".to_owned(),
+        })),
+    }
+}
 pub async fn get_payment_response_using_payment_get_operation(
     state: &SessionState,
     payment_intent_id: &id_type::GlobalPaymentId,
diff --git a/crates/router/src/routes/app.rs b/crates/router/src/routes/app.rs
index 56ebfa66062..1de0029f767 100644
--- a/crates/router/src/routes/app.rs
+++ b/crates/router/src/routes/app.rs
@@ -2944,8 +2944,16 @@ impl ProcessTracker {
         web::scope("/v2/process-trackers/revenue-recovery-workflow")
             .app_data(web::Data::new(state.clone()))
             .service(
-                web::resource("/{revenue_recovery_id}")
-                    .route(web::get().to(revenue_recovery::revenue_recovery_pt_retrieve_api)),
+                web::scope("/{revenue_recovery_id}")
+                    .service(
+                        web::resource("").route(
+                            web::get().to(revenue_recovery::revenue_recovery_pt_retrieve_api),
+                        ),
+                    )
+                    .service(
+                        web::resource("/resume")
+                            .route(web::post().to(revenue_recovery::revenue_recovery_resume_api)),
+                    ),
             )
     }
 }
diff --git a/crates/router/src/routes/lock_utils.rs b/crates/router/src/routes/lock_utils.rs
index 63972a938fe..992edb61c35 100644
--- a/crates/router/src/routes/lock_utils.rs
+++ b/crates/router/src/routes/lock_utils.rs
@@ -63,11 +63,9 @@ impl From<Flow> for ApiIdentifier {
             | Flow::MerchantTransferKey
             | Flow::MerchantAccountList
             | Flow::EnablePlatformAccount => Self::MerchantAccount,
-
             Flow::OrganizationCreate | Flow::OrganizationRetrieve | Flow::OrganizationUpdate => {
                 Self::Organization
             }
-
             Flow::RoutingCreateConfig
             | Flow::RoutingLinkConfig
             | Flow::RoutingUnlinkConfig
@@ -93,36 +91,29 @@ impl From<Flow> for ApiIdentifier {
             Flow::CreateSubscription => Self::Subscription,
 
             Flow::RetrieveForexFlow => Self::Forex,
-
             Flow::AddToBlocklist => Self::Blocklist,
             Flow::DeleteFromBlocklist => Self::Blocklist,
             Flow::ListBlocklist => Self::Blocklist,
             Flow::ToggleBlocklistGuard => Self::Blocklist,
-
             Flow::MerchantConnectorsCreate
             | Flow::MerchantConnectorsRetrieve
             | Flow::MerchantConnectorsUpdate
             | Flow::MerchantConnectorsDelete
             | Flow::MerchantConnectorsList => Self::MerchantConnector,
-
             Flow::ConfigKeyCreate
             | Flow::ConfigKeyFetch
             | Flow::ConfigKeyUpdate
             | Flow::ConfigKeyDelete
             | Flow::CreateConfigKey => Self::Configs,
-
             Flow::CustomersCreate
             | Flow::CustomersRetrieve
             | Flow::CustomersUpdate
             | Flow::CustomersDelete
             | Flow::CustomersGetMandates
             | Flow::CustomersList => Self::Customers,
-
             Flow::EphemeralKeyCreate | Flow::EphemeralKeyDelete => Self::Ephemeral,
-
             Flow::DeepHealthCheck | Flow::HealthCheck => Self::Health,
             Flow::MandatesRetrieve | Flow::MandatesRevoke | Flow::MandatesList => Self::Mandates,
-
             Flow::PaymentMethodsCreate
             | Flow::PaymentMethodsMigrate
             | Flow::PaymentMethodsBatchUpdate
@@ -138,9 +129,7 @@ impl From<Flow> for ApiIdentifier {
             | Flow::DefaultPaymentMethodsSet
             | Flow::PaymentMethodSave
             | Flow::TotalPaymentMethodCount => Self::PaymentMethods,
-
             Flow::PmAuthLinkTokenCreate | Flow::PmAuthExchangeToken => Self::PaymentMethodAuth,
-
             Flow::PaymentsCreate
             | Flow::PaymentsRetrieve
             | Flow::PaymentsRetrieveForceSync
@@ -177,7 +166,6 @@ impl From<Flow> for ApiIdentifier {
             | Flow::PaymentsRetrieveUsingMerchantReferenceId
             | Flow::PaymentAttemptsList
             | Flow::RecoveryPaymentsCreate => Self::Payments,
-
             Flow::PayoutsCreate
             | Flow::PayoutsRetrieve
             | Flow::PayoutsUpdate
@@ -188,7 +176,6 @@ impl From<Flow> for ApiIdentifier {
             | Flow::PayoutsAccounts
             | Flow::PayoutsConfirm
             | Flow::PayoutLinkInitiate => Self::Payouts,
-
             Flow::RefundsCreate
             | Flow::RefundsRetrieve
             | Flow::RefundsRetrieveForceSync
@@ -198,7 +185,6 @@ impl From<Flow> for ApiIdentifier {
             | Flow::RefundsAggregate
             | Flow::RefundsManualUpdate => Self::Refunds,
             Flow::Relay | Flow::RelayRetrieve => Self::Relay,
-
             Flow::FrmFulfillment
             | Flow::IncomingWebhookReceive
             | Flow::IncomingRelayWebhookReceive
@@ -207,13 +193,11 @@ impl From<Flow> for ApiIdentifier {
             | Flow::WebhookEventDeliveryRetry
             | Flow::RecoveryIncomingWebhookReceive
             | Flow::IncomingNetworkTokenWebhookReceive => Self::Webhooks,
-
             Flow::ApiKeyCreate
             | Flow::ApiKeyRetrieve
             | Flow::ApiKeyUpdate
             | Flow::ApiKeyRevoke
             | Flow::ApiKeyList => Self::ApiKeys,
-
             Flow::DisputesRetrieve
             | Flow::DisputesList
             | Flow::DisputesFilters
@@ -222,16 +206,12 @@ impl From<Flow> for ApiIdentifier {
             | Flow::RetrieveDisputeEvidence
             | Flow::DisputesAggregate
             | Flow::DeleteDisputeEvidence => Self::Disputes,
-
             Flow::CardsInfo
             | Flow::CardsInfoCreate
             | Flow::CardsInfoUpdate
             | Flow::CardsInfoMigrate => Self::CardsInfo,
-
             Flow::CreateFile | Flow::DeleteFile | Flow::RetrieveFile => Self::Files,
-
             Flow::CacheInvalidate => Self::Cache,
-
             Flow::ProfileCreate
             | Flow::ProfileUpdate
             | Flow::ProfileRetrieve
@@ -239,23 +219,18 @@ impl From<Flow> for ApiIdentifier {
             | Flow::ProfileList
             | Flow::ToggleExtendedCardInfo
             | Flow::ToggleConnectorAgnosticMit => Self::Profile,
-
             Flow::PaymentLinkRetrieve
             | Flow::PaymentLinkInitiate
             | Flow::PaymentSecureLinkInitiate
             | Flow::PaymentLinkList
             | Flow::PaymentLinkStatus => Self::PaymentLink,
-
             Flow::Verification => Self::Verification,
-
             Flow::RustLockerMigration => Self::RustLockerMigration,
             Flow::GsmRuleCreate
             | Flow::GsmRuleRetrieve
             | Flow::GsmRuleUpdate
             | Flow::GsmRuleDelete => Self::Gsm,
-
             Flow::ApplePayCertificatesMigration => Self::ApplePayCertificatesMigration,
-
             Flow::UserConnectAccount
             | Flow::UserSignUp
             | Flow::UserSignIn
@@ -341,44 +316,34 @@ impl From<Flow> for ApiIdentifier {
             | Flow::UpdateRole
             | Flow::UserFromEmail
             | Flow::ListUsersInLineage => Self::UserRole,
-
             Flow::GetActionUrl | Flow::SyncOnboardingStatus | Flow::ResetTrackingId => {
                 Self::ConnectorOnboarding
             }
-
             Flow::ReconMerchantUpdate
             | Flow::ReconTokenRequest
             | Flow::ReconServiceRequest
             | Flow::ReconVerifyToken => Self::Recon,
-
             Flow::RetrievePollStatus => Self::Poll,
-
             Flow::FeatureMatrix => Self::Documentation,
-
             Flow::TokenizeCard
             | Flow::TokenizeCardUsingPaymentMethodId
             | Flow::TokenizeCardBatch => Self::CardNetworkTokenization,
-
             Flow::HypersenseTokenRequest
             | Flow::HypersenseVerifyToken
             | Flow::HypersenseSignoutToken => Self::Hypersense,
-
             Flow::PaymentMethodSessionCreate
             | Flow::PaymentMethodSessionRetrieve
             | Flow::PaymentMethodSessionConfirm
             | Flow::PaymentMethodSessionUpdateSavedPaymentMethod
             | Flow::PaymentMethodSessionDeleteSavedPaymentMethod
             | Flow::PaymentMethodSessionUpdate => Self::PaymentMethodSession,
-
-            Flow::RevenueRecoveryRetrieve => Self::ProcessTracker,
-
+            Flow::RevenueRecoveryRetrieve | Flow::RevenueRecoveryResume => Self::ProcessTracker,
             Flow::AuthenticationCreate
             | Flow::AuthenticationEligibility
             | Flow::AuthenticationSync
             | Flow::AuthenticationSyncPostUpdate
             | Flow::AuthenticationAuthenticate => Self::Authentication,
             Flow::Proxy => Self::Proxy,
-
             Flow::ProfileAcquirerCreate | Flow::ProfileAcquirerUpdate => Self::ProfileAcquirer,
             Flow::ThreeDsDecisionRuleExecute => Self::ThreeDsDecisionRule,
             Flow::TokenizationCreate | Flow::TokenizationRetrieve | Flow::TokenizationDelete => {
diff --git a/crates/router/src/routes/process_tracker/revenue_recovery.rs b/crates/router/src/routes/process_tracker/revenue_recovery.rs
index b0deb62fa87..db939436c61 100644
--- a/crates/router/src/routes/process_tracker/revenue_recovery.rs
+++ b/crates/router/src/routes/process_tracker/revenue_recovery.rs
@@ -37,3 +37,27 @@ pub async fn revenue_recovery_pt_retrieve_api(
     ))
     .await
 }
+
+pub async fn revenue_recovery_resume_api(
+    state: web::Data<AppState>,
+    req: HttpRequest,
+    path: web::Path<common_utils::id_type::GlobalPaymentId>,
+    json_payload: web::Json<revenue_recovery_api::RevenueRecoveryRetriggerRequest>,
+) -> HttpResponse {
+    let flow = Flow::RevenueRecoveryResume;
+    let id = path.into_inner();
+    let payload = json_payload.into_inner();
+
+    Box::pin(api::server_wrap(
+        flow,
+        state,
+        &req,
+        payload,
+        |state, _, payload, _| {
+            revenue_recovery::resume_revenue_recovery_process_tracker(state, id.clone(), payload)
+        },
+        &auth::V2AdminApiAuth,
+        api_locking::LockAction::NotApplicable,
+    ))
+    .await
+}
diff --git a/crates/router_env/src/logger/types.rs b/crates/router_env/src/logger/types.rs
index ace7635bf40..50c47489108 100644
--- a/crates/router_env/src/logger/types.rs
+++ b/crates/router_env/src/logger/types.rs
@@ -620,6 +620,8 @@ pub enum Flow {
     TotalPaymentMethodCount,
     /// Process Tracker Revenue Recovery Workflow Retrieve
     RevenueRecoveryRetrieve,
+    /// Process Tracker Revenue Recovery Workflow Resume
+    RevenueRecoveryResume,
     /// Tokenization flow
     TokenizationCreate,
     /// Tokenization retrieve flow

## Type of Change
<!-- Put an `x` in the boxes that apply -->

- [ ] Bugfix
- [ ] New feature
- [ ] Enhancement
- [ ] Refactoring
- [ ] Dependency updates
- [ ] Documentation
- [ ] CI/CD

## Description
 Add resume api to resume/continue the tasks in process tracker for revenue_recovery. This PR enables us to resume the EXECUTE and CALCULATE  that are stuck in an intermediate state .


### Additional Changes

- [ ] This PR modifies the API contract
- [ ] This PR modifies the database schema
- [ ] This PR modifies application configuration/environment variables

<!--
Provide links to the files with corresponding changes.

Following are the paths where you can find config files:
1. `config`
2. `crates/router/src/configs`
3. `loadtest/config`
-->


## Motivation and Context
<!--
Why is this change required? What problem does it solve?
If it fixes an open issue, please link to the issue here.

If you don't have an issue, we'd recommend starting with one first so the PR
can focus on the implementation (unless it is an obvious bug or documentation fix
that will have little conversation).
-->


## How did you test it?
- Create a MA and MCA and Billing MCA
- Create an intent and initiate a process tracker entry
- We can change the process tracker schedule time for revenue_recovery workflows , to resume some jobs
```curl
curl --location 'http://localhost:8080/v2/process-trackers/revenue-recovery-workflow/12345_pay_01997ac9a9a87612b86629dad872b519/resume' \
--header 'Content-Type: application/json' \
--header 'Authorization: admin-api-key=test_admin' \
--header 'api-key: dev_zxcOiG9TUvXT2jtzQAMLGllDYWw5KpXJyxTlGxy1SjjJ2Y5ScXLhKUfQK2PHUHSz' \
--data '{
    "revenue_recovery_task": "EXECUTE_WORKFLOW",
    "schedule_time": "2025-08-28T12:44:44Z",
    "status": "pending",
    "business_status": "Pending"
}'
````
<img width="3456" height="2234" alt="Screenshot 2025-09-24 at 1 47 02 PM" src="https://github.com/user-attachments/assets/1928497f-02c7-4079-9813-f56737c9fb74" />

## Checklist
<!-- Put an `x` in the boxes that apply -->

- [ ] I formatted the code `cargo +nightly fmt --all`
- [ ] I addressed lints thrown by `cargo clippy`
- [ ] I reviewed the submitted code
- [ ] I added unit tests for my changes where possible
