Skip to main content

What are workflows?

Workflows are multi-step automation templates in Skyvern that chain together multiple operations into a reusable, parameterized process. While a single Task handles one goal on one page, a Workflow orchestrates an entire end-to-end process: logging in, navigating multiple pages, extracting data, downloading files, and sending notifications. You define a workflow once with blocks and parameters, then run it repeatedly with different input values each time.

Workflows vs Tasks

Understanding when to use a workflow versus a task is critical for building effective automations.
CharacteristicTasksWorkflows
StructureSingle prompt, one executionMultiple blocks in sequence
ScopeSingle goal, usually one pageMulti-step process across pages
ReusabilityRun ad-hoc or scheduledDefine once, run with different parameters
ParameterizationBasic (prompt, URL, extraction schema)Rich (input parameters, block outputs, Jinja templates)
Control FlowLinearLoops, conditionals, validation gates
Data PassingOutput returned at the endEach block can pass data to subsequent blocks
CompositionSingle AI agent executionCombine navigation, extraction, files, notifications
File OperationsDownload files during taskDownload, parse, upload, email files across blocks
Best ForPrototyping, simple extractions, one-offsProduction automations, bulk processing, complex logic
Run ID Formattsk_*wr_*

When to use Tasks

  • Quick prototyping: Testing if Skyvern can interact with a specific website
  • Simple data extraction: Getting product prices, contact information, or table data from a single page
  • One-off automations: Ad-hoc tasks that don’t need to be repeated with different inputs
  • Single-page operations: Filling a form, clicking a button, or extracting data from one page

When to use Workflows

  • Production automations: Processes that run regularly with different inputs
  • Multi-step processes: Login → navigate → extract → download → notify
  • Bulk operations: Processing lists of items (job applications, invoice downloads, form submissions)
  • Complex logic: Conditional branching, validation gates, error handling
  • File processing: Parse CSV/PDF inputs, download files, upload to storage, email attachments
  • Data pipelines: Extract from multiple sources, transform data, send to webhooks or APIs

Workflow Architecture

A workflow consists of three core components:

1. Parameters

Parameters are the inputs your workflow accepts. They make workflows reusable by allowing different values on each run.
parameters:
  - key: company_name
    parameter_type: workflow
    workflow_parameter_type: string
  - key: ein_info
    parameter_type: workflow
    workflow_parameter_type: json
  - key: recipient_email
    parameter_type: workflow
    workflow_parameter_type: string
Parameters are referenced throughout the workflow using Jinja2 template syntax: {{ company_name }}, {{ ein_info.tax_id }}, etc.

2. Blocks

Blocks are the individual steps that execute in sequence. Each block has a unique label and a block_type that determines its function.
blocks:
  - label: fill_ss4_form
    block_type: navigation
    url: "https://sa.www4.irs.gov/modiein/individual/index.jsp"
    navigation_goal: Fill out the SS-4 form using {{ ein_info }}

  - label: extract_ein
    block_type: extraction
    data_extraction_goal: Extract the EIN number from the confirmation page
    data_schema:
      type: object
      properties:
        ein_number:
          type: string
Skyvern supports 23 block types including:
  • Navigation & interaction: navigation, action, login, goto_url
  • Data operations: extraction, validation, text_prompt
  • Control flow: for_loop, conditional, validation
  • File operations: file_download, file_url_parser, upload_to_s3, print_page
  • Integrations: http_request, send_email, human_interaction
  • Utilities: code, wait
See Workflow Blocks Reference for complete details on all block types.

3. Context & Data Flow

Each block produces an output that becomes available to subsequent blocks. Reference block outputs using {{ label_output }}:
blocks:
  - label: parse_resume
    block_type: file_url_parser
    file_url: "{{ resume_url }}"
    file_type: pdf

  - label: apply_to_job
    block_type: navigation
    url: "{{ job_url }}"
    navigation_goal: |
      Fill out the application form with:
      - Name: {{ parse_resume_output.name }}
      - Email: {{ parse_resume_output.email }}
      - Experience: {{ parse_resume_output.work_experience }}
The parse_resume block extracts structured data from a PDF, and the apply_to_job block uses that data to fill out a form.

Workflow Execution Model

Sequential Execution

By default, blocks execute in the order they’re defined:
Block 1 → Block 2 → Block 3 → Block 4
You can override sequential order using next_block_label:
blocks:
  - label: login
    block_type: login
    next_block_label: extract_data  # Skip to extract_data

  - label: validate  # This gets skipped
    block_type: validation

  - label: extract_data
    block_type: extraction

Conditional Branching

Use conditional blocks to create branching logic:
- label: check_status
  block_type: conditional
  branch_conditions:
    - criteria:
        criteria_type: jinja2_template
        expression: "{{ status == 'approved' }}"
      next_block_label: process_approval
    - criteria:
        criteria_type: jinja2_template
        expression: "{{ status == 'rejected' }}"
      next_block_label: handle_rejection
    - is_default: true
      next_block_label: wait_for_review

Looping

Use for_loop blocks to iterate over arrays:
- label: process_orders
  block_type: for_loop
  loop_over_parameter_key: order_ids
  loop_blocks:
    - label: download_invoice
      block_type: file_download
      url: "https://example.com/invoice/{{ download_invoice.current_value }}"
      navigation_goal: Download the invoice PDF
The for_loop executes the nested blocks once for each item in the array.

Error Handling

Control workflow behavior on block failure:
- label: optional_step
  block_type: navigation
  continue_on_failure: true  # Continue even if this block fails

- label: critical_step
  block_type: extraction
  continue_on_failure: false  # Stop workflow if this fails (default)
Inside loops, use next_loop_on_failure to skip to the next iteration on failure:
- label: process_items
  block_type: for_loop
  loop_over_parameter_key: items
  loop_blocks:
    - label: process_item
      block_type: navigation
      next_loop_on_failure: true  # Skip to next item on failure

Finally Blocks

Designate a block to always execute when the workflow ends (success or failure):
workflow_definition:
  parameters: [...]
  blocks:
    - label: main_task
      block_type: navigation
      # ...
    - label: send_notification
      block_type: send_email
      # ...
  finally_block_label: send_notification  # Always runs
The finally block executes regardless of whether the workflow completed, failed, terminated, or timed out (but not if explicitly canceled).

Workflow Lifecycle

1. Creation

Define the workflow structure and submit it to Skyvern:
workflow = await client.create_workflow(
    json_definition={
        "title": "Invoice Processor",
        "workflow_definition": {
            "parameters": [...],
            "blocks": [...]
        }
    }
)
# Returns: workflow_permanent_id (e.g., wpid_123456789)
The workflow definition is validated and stored. The workflow_permanent_id is used to run the workflow.

2. Execution

Run the workflow with specific parameter values:
run = await client.run_workflow(
    workflow_id="wpid_123456789",
    parameters={
        "vendor_name": "Acme Corp",
        "start_date": "2026-01-01"
    }
)
# Returns: run_id (e.g., wr_486305187432193510)
The workflow executes asynchronously in the background.

3. Monitoring

Check status and retrieve results:
result = await client.get_run(run_id)
print(result.status)  # created, running, completed, failed, etc.
print(result.output)  # Structured output from all blocks
Or use webhooks to get notified when the workflow completes.

Workflow State & Browser Sessions

Browser Context

By default, each block in a workflow uses a fresh browser context. Set persist_browser_session: true to share cookies, login state, and local storage across all blocks:
workflow = await client.create_workflow(
    json_definition={
        "title": "Multi-Page Scraper",
        "persist_browser_session": True,  # Blocks share browser state
        "workflow_definition": {...}
    }
)
This is useful for workflows that log in once and then navigate multiple pages.

Reusing Browser Sessions

Pass a browser_session_id when running a workflow to continue from an existing browser state:
run = await client.run_workflow(
    workflow_id="wpid_123456789",
    browser_session_id="pbs_abc123",  # Reuse existing session
    parameters={...}
)

Browser Profiles

Use browser_profile_id to persist cookies and storage across multiple workflow runs:
run = await client.run_workflow(
    workflow_id="wpid_123456789",
    browser_profile_id="bp_xyz789",  # Reuse saved profile
    parameters={...}
)

Best Practices

1. Design for Reusability

Parameterize everything that might change:
# Bad: Hardcoded values
blocks:
  - label: login
    block_type: login
    url: "https://portal.acmecorp.com"
    parameter_keys:
      - credentials

# Good: Parameterized URL
parameters:
  - key: portal_url
    parameter_type: workflow
    workflow_parameter_type: string

blocks:
  - label: login
    block_type: login
    url: "{{ portal_url }}"
    parameter_keys:
      - credentials

2. Use Descriptive Labels

Block labels become part of the output structure:
# Bad: Generic labels
- label: step1
  block_type: extraction

# Good: Descriptive labels
- label: extract_ein_number
  block_type: extraction

3. Validate Critical Data

Use validation blocks to check assumptions:
- label: validate_form_submission
  block_type: validation
  complete_criterion: "The page shows 'Application Submitted Successfully'"
  terminate_criterion: "The page shows an error message"

4. Structure Complex Goals

Break complex navigation into clear steps:
navigation_goal: |
  Fill out the job application form.

  Use this information:
  - Name: {{ resume_output.name }}
  - Email: {{ resume_output.email }}
  - Phone: {{ resume_output.phone }}

  COMPLETE when the form is submitted.
  TERMINATE if the position is no longer available.

5. Handle Errors Gracefully

Use continue_on_failure and error_code_mapping for resilient workflows:
- label: download_optional_attachment
  block_type: file_download
  continue_on_failure: true  # Don't fail workflow if attachment is missing
  error_code_mapping:
    NO_DOWNLOAD_LINK: "The page does not contain a download link"

Next Steps

Creating Workflows

Learn how to create workflows via SDK, API, and UI

Workflow Blocks

Complete reference of all 23 workflow block types

Parameters & Context

Master parameter types and data flow between blocks

Running Workflows

Execute workflows and retrieve results

Build docs developers (and LLMs) love