> ## Documentation Index
> Fetch the complete documentation index at: https://wiki.latch.bio/llms.txt
> Use this file to discover all available pages before exploring further.

# Programmatic Execution

> Execute a workflow programmatically from python.

<Note>Available in `latch >= 2.62.0`</Note>

The Latch SDK provides a Python API to programmatically launch workflows and monitor their status. This is useful for integrating workflows into automated test suites (e.g., GitHub CI/CD).

There are two ways to launch a workflow programmatically:

1. Call the workflow with Python parameter values.
2. Use a `LaunchPlan` from a previously registered workflow.

## Pre-requisites

* Please make sure you have registered your workflow to Latch using `latch register`. This creates a new workflow version, which you will reference in your script.

## Usage

<Tabs>
  <Tab title="Using Python parameter values">
    ```python expandable theme={null}
    from latch.types.file import LatchFile
    from latch_cli.services.launch.launch_v2 import launch
    from wf.entrypoint import GenomeReference, Sample

    # Using python parameter values
    execution_id = launch(
        wf_name="my_workflow",
        version="0.1.0",
        params={
            "input": [
                Sample(
                    patient="TestSample",
                    fastq_1=LatchFile(
                        "latch:///test_L1_1.fastq.gz"
                    ),
                    ...
                ),
                ...
            ],
            "genome": GenomeReference.GATK_GRCh37,
            ...
        }
    )
    ```

    **Parameters**:

    * `wf_name:` Name of the workflow function. This is the function defined immediately after the `@workflow` decorator in your code ([Example](https://github.com/latchbio-nfcore/methylseq/blob/96d58c8532a5c2ae6204e21209f71148bc78dc1e/wf/__init__.py#L20))
    * `version`: Workflow version registered on Latch
    * `params`: Dictionary of parameter names and values. If the python values are not typed using the exact same types used in the workflow function signature (including the module paths), the `best_effort` parameter should be set to `True`.
    * `best_effort` (default True): (available in `latch >= 2.67.8`) When set to `True`, allows for flexible conversion of params to workflow inputs. This enables launching workflows outside of the workflow environment with compatible values (e.g., strings for enums where the string matches an enum option, or dictionaries/dataclasses with all the fields of a particular dataclass).
  </Tab>

  <Tab title="Using a previously registered LaunchPlan">
    ```python expandable theme={null}

      import email.utils
      import time
      from datetime import datetime

      from latch_cli.services.launch.launch_v2 import launch_from_launch_plan
      from latch_cli.tinyrequests import post
      from latch_cli.utils import get_auth_header
      from latch_sdk_config.latch import config

      # Using a previously registered LaunchPlan
      execution_id = launch_from_launch_plan(
          wf_name="my_workflow",
          version="0.1.0",
          lp_name="Test Data",
      )

      # Polling execution status
      while True:
          list_resp = post(
              url=config.api.execution.list,
              headers = {"Authorization": get_auth_header()},
              json={"ws_account_id": "XXXXX"},
          ).json()

          target_execution = list_resp[str(execution_id)]
          target_execution_status: str = target_execution.get('status')

          if target_execution_status == 'SUCCEEDED':
              break
          elif target_execution_status == 'FAILED':
              raise Exception("Execution failed")
          elif target_execution_status == 'ABORTED':
              raise Exception("Execution aborted")
          elif target_execution_status == 'UNDEFINED':
              time.sleep(5)
              continue

          start_time_str: str = target_execution.get('start_time')
          start_time_tuple = email.utils.parsedate_tz(start_time_str)
          if start_time_tuple is None:
              raise Exception("Failed to parse start time")

          start_time = datetime.fromtimestamp(email.utils.mktime_tz(start_time_tuple))
          current_time = datetime.now()

          if (current_time - start_time).total_seconds() > 300:
              raise TimeoutError("Execution has been running for more than 5 minutes")

          time.sleep(10)
    ```

    **Parameters**:

    * `wf_name:` Name of the workflow function. This is the function defined immediately after the `@workflow` decorator in your code ([Example](https://github.com/latchbio-nfcore/methylseq/blob/96d58c8532a5c2ae6204e21209f71148bc78dc1e/wf/__init__.py#L20))
    * `version`: Workflow version registered on Latch
    * `lp_name`: Name of the LaunchPlan ([Example](https://github.com/latchbio-nfcore/methylseq/blob/96d58c8532a5c2ae6204e21209f71148bc78dc1e/wf/__init__.py#L252))
  </Tab>
</Tabs>

<Note>
  **Important note on Python version**: If the Python version in the workflow's
  Docker image does not match the Python version running the script that calls
  `launch` or `launch_from_launch_plan,` the function may fail with a dill
  unpickling error. In this case, it is recommended to use the same Python
  version used during workflow registration.
</Note>
