Skip to main content

Overview

Workflows are created by defining a structure with parameters and blocks, then submitting it to Skyvern. Once created, a workflow can be run multiple times with different parameter values. You can create workflows through:
  • Python SDK: Best for programmatic workflow management
  • TypeScript SDK: For JavaScript/TypeScript applications
  • REST API: Language-agnostic HTTP interface
  • Skyvern UI: Visual workflow builder (recommended for getting started)

Workflow Structure

Every workflow definition contains:
title: "Workflow Name"                 # Display name
description: "What this workflow does" # Optional description
workflow_definition:
  parameters:                           # Input parameters
    - key: param_name
      parameter_type: workflow
      workflow_parameter_type: string
  blocks:                               # Execution steps
    - label: step_name
      block_type: navigation
      # ... block-specific fields

JSON vs YAML Format

Skyvern accepts workflow definitions in two formats:
  1. JSON format via json_definition (structured object)
  2. YAML format via yaml_definition (string)
Both formats represent the same structure. Use JSON for programmatic generation or YAML for readability.

Creating via Python SDK

Installation

pip install skyvern

Basic Example

import os
import asyncio
from skyvern import Skyvern

async def main():
    client = Skyvern(api_key=os.getenv("SKYVERN_API_KEY"))

    workflow = await client.create_workflow(
        json_definition={
            "title": "Invoice Downloader",
            "description": "Download invoices from vendor portal",
            "workflow_definition": {
                "parameters": [
                    {
                        "key": "portal_url",
                        "parameter_type": "workflow",
                        "workflow_parameter_type": "string"
                    },
                    {
                        "key": "start_date",
                        "parameter_type": "workflow",
                        "workflow_parameter_type": "string"
                    }
                ],
                "blocks": [
                    {
                        "label": "login_to_portal",
                        "block_type": "login",
                        "url": "{{ portal_url }}",
                        "parameter_keys": ["credentials"]
                    },
                    {
                        "label": "navigate_to_invoices",
                        "block_type": "navigation",
                        "navigation_goal": "Navigate to the invoices section and filter by date >= {{ start_date }}"
                    },
                    {
                        "label": "download_invoices",
                        "block_type": "file_download",
                        "navigation_goal": "Download all invoice PDFs"
                    }
                ]
            }
        }
    )

    print(f"Created workflow: {workflow.workflow_permanent_id}")
    return workflow

asyncio.run(main())

Using YAML Format

For better readability, use YAML:
workflow = await client.create_workflow(
    yaml_definition="""|
title: Invoice Downloader
description: Download invoices from vendor portal
workflow_definition:
  parameters:
    - key: portal_url
      parameter_type: workflow
      workflow_parameter_type: string
    - key: start_date
      parameter_type: workflow
      workflow_parameter_type: string
  blocks:
    - label: login_to_portal
      block_type: login
      url: "{{ portal_url }}"
      parameter_keys:
        - credentials
    - label: navigate_to_invoices
      block_type: navigation
      navigation_goal: |
        Navigate to the invoices section.
        Filter by date >= {{ start_date }}.
    - label: download_invoices
      block_type: file_download
      navigation_goal: Download all invoice PDFs
"""
)

Loading from File

with open("workflows/invoice_downloader.yaml", "r") as f:
    workflow_yaml = f.read()

workflow = await client.create_workflow(yaml_definition=workflow_yaml)

Creating via TypeScript SDK

Installation

npm install @skyvern/client

Basic Example

import { SkyvernClient } from "@skyvern/client";

async function main() {
  const client = new SkyvernClient({
    apiKey: process.env.SKYVERN_API_KEY,
  });

  const workflow = await client.createWorkflow({
    body: {
      json_definition: {
        title: "Invoice Downloader",
        description: "Download invoices from vendor portal",
        workflow_definition: {
          parameters: [
            {
              key: "portal_url",
              parameter_type: "workflow",
              workflow_parameter_type: "string",
            },
            {
              key: "start_date",
              parameter_type: "workflow",
              workflow_parameter_type: "string",
            },
          ],
          blocks: [
            {
              label: "login_to_portal",
              block_type: "login",
              url: "{{ portal_url }}",
              parameter_keys: ["credentials"],
            },
            {
              label: "navigate_to_invoices",
              block_type: "navigation",
              navigation_goal:
                "Navigate to the invoices section and filter by date >= {{ start_date }}",
            },
            {
              label: "download_invoices",
              block_type: "file_download",
              navigation_goal: "Download all invoice PDFs",
            },
          ],
        },
      },
    },
  });

  console.log(`Created workflow: ${workflow.workflow_permanent_id}`);
  return workflow;
}

main();

Creating via REST API

Endpoint

POST https://api.skyvern.com/v1/workflows

Headers

x-api-key: <your-api-key>
Content-Type: application/json

Request Body

{
  "json_definition": {
    "title": "Invoice Downloader",
    "description": "Download invoices from vendor portal",
    "workflow_definition": {
      "parameters": [
        {
          "key": "portal_url",
          "parameter_type": "workflow",
          "workflow_parameter_type": "string"
        },
        {
          "key": "start_date",
          "parameter_type": "workflow",
          "workflow_parameter_type": "string"
        }
      ],
      "blocks": [
        {
          "label": "login_to_portal",
          "block_type": "login",
          "url": "{{ portal_url }}",
          "parameter_keys": ["credentials"]
        },
        {
          "label": "navigate_to_invoices",
          "block_type": "navigation",
          "navigation_goal": "Navigate to the invoices section and filter by date >= {{ start_date }}"
        },
        {
          "label": "download_invoices",
          "block_type": "file_download",
          "navigation_goal": "Download all invoice PDFs"
        }
      ]
    }
  }
}

Example with cURL

curl -X POST "https://api.skyvern.com/v1/workflows" \
  -H "x-api-key: $SKYVERN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "json_definition": {
      "title": "Invoice Downloader",
      "workflow_definition": {
        "parameters": [
          {
            "key": "portal_url",
            "parameter_type": "workflow",
            "workflow_parameter_type": "string"
          }
        ],
        "blocks": [
          {
            "label": "login",
            "block_type": "login",
            "url": "{{ portal_url }}",
            "parameter_keys": ["credentials"]
          }
        ]
      }
    }
  }'

Real-World Examples

Example 1: Job Application Workflow

This workflow parses a resume and applies to multiple job postings:
workflow = await client.create_workflow(
    json_definition={
        "title": "Bulk Job Application",
        "workflow_definition": {
            "parameters": [
                {
                    "key": "resume",
                    "parameter_type": "workflow",
                    "workflow_parameter_type": "file_url",
                    "description": "URL to candidate's resume PDF"
                },
                {
                    "key": "job_urls",
                    "parameter_type": "workflow",
                    "workflow_parameter_type": "json",
                    "description": "Array of job posting URLs"
                },
                {
                    "key": "cover_letter",
                    "parameter_type": "workflow",
                    "workflow_parameter_type": "string",
                    "default_value": ""
                }
            ],
            "blocks": [
                {
                    "label": "parse_resume",
                    "block_type": "file_url_parser",
                    "file_url": "{{ resume }}",
                    "file_type": "pdf",
                    "json_schema": {
                        "type": "object",
                        "properties": {
                            "name": {"type": "string"},
                            "email": {"type": "string"},
                            "phone": {"type": "string"},
                            "work_experience": {"type": "array"}
                        }
                    }
                },
                {
                    "label": "apply_to_jobs",
                    "block_type": "for_loop",
                    "loop_over_parameter_key": "job_urls",
                    "continue_on_failure": True,
                    "loop_blocks": [
                        {
                            "label": "submit_application",
                            "block_type": "navigation",
                            "url": "{{ submit_application.current_value }}",
                            "navigation_goal": (
                                "Fill out and submit the job application.\n\n"
                                "Use this information:\n"
                                "- Name: {{ parse_resume_output.name }}\n"
                                "- Email: {{ parse_resume_output.email }}\n"
                                "- Phone: {{ parse_resume_output.phone }}\n"
                                "- Experience: {{ parse_resume_output.work_experience }}\n"
                                "- Cover Letter: {{ cover_letter }}\n\n"
                                "COMPLETE when the application is successfully submitted."
                            )
                        }
                    ]
                }
            ]
        }
    }
)

Example 2: IRS EIN Registration

A workflow that fills out the IRS SS-4 form, validates the submission, extracts the EIN, and emails the confirmation:
workflow = await client.create_workflow(
    yaml_definition="""|
title: IRS EIN Registration
description: Complete IRS Form SS-4 for EIN registration
workflow_definition:
  parameters:
    - key: ein_info
      parameter_type: workflow
      workflow_parameter_type: json
      description: Company information for EIN application
    - key: recipient_email
      parameter_type: workflow
      workflow_parameter_type: string

  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 with the following information:
        {{ ein_info }}

        COMPLETE when you reach the review/summary page.
        TERMINATE if the form submission fails.
      max_steps_per_run: 30

    - label: validate_submission
      block_type: validation
      complete_criterion: |
        The summary page shows all submitted information correctly.
      terminate_criterion: |
        The page shows an error or the information is incorrect.

    - label: extract_ein
      block_type: extraction
      data_extraction_goal: |
        Extract the assigned EIN number and legal entity name.
      data_schema:
        type: object
        properties:
          ein_number:
            type: string
          legal_name:
            type: string

    - label: download_confirmation
      block_type: file_download
      navigation_goal: |
        Download the EIN confirmation letter PDF.
      download_suffix: ein_confirmation.pdf

    - label: send_email
      block_type: send_email
      smtp_host_secret_parameter_key: smtp_host
      smtp_port_secret_parameter_key: smtp_port
      smtp_username_secret_parameter_key: smtp_username
      smtp_password_secret_parameter_key: smtp_password
      sender: [email protected]
      recipients:
        - "{{ recipient_email }}"
      subject: "EIN Registration Complete - {{ extract_ein_output.legal_name }}"
      body: |
        Your EIN registration has been completed.

        EIN: {{ extract_ein_output.ein_number }}
        Legal Name: {{ extract_ein_output.legal_name }}

        See attached for the confirmation letter.
      file_attachments:
        - SKYVERN_DOWNLOAD_DIRECTORY
"""
)

Example 3: Credential Verification

A workflow that checks professional credentials across multiple registries:
workflow = await client.create_workflow(
    json_definition={
        "title": "ASHA Credential Verification",
        "workflow_definition": {
            "parameters": [
                {
                    "key": "first_name",
                    "parameter_type": "workflow",
                    "workflow_parameter_type": "string"
                },
                {
                    "key": "last_name",
                    "parameter_type": "workflow",
                    "workflow_parameter_type": "string"
                },
                {
                    "key": "state",
                    "parameter_type": "workflow",
                    "workflow_parameter_type": "string"
                }
            ],
            "blocks": [
                {
                    "label": "search_registry",
                    "block_type": "navigation",
                    "url": "https://asha.org/verify",
                    "navigation_goal": (
                        "Search for the person using:\n"
                        "- First name: {{ first_name }}\n"
                        "- Last name: {{ last_name }}\n"
                        "- State: {{ state }}\n\n"
                        "COMPLETE when search results are displayed.\n"
                        "TERMINATE if no results found."
                    ),
                    "parameter_keys": ["first_name", "last_name", "state"]
                },
                {
                    "label": "extract_credentials",
                    "block_type": "extraction",
                    "data_extraction_goal": (
                        "Extract professional credentials including:\n"
                        "- ASHA account number\n"
                        "- Certification status\n"
                        "- Valid through date"
                    ),
                    "data_schema": {
                        "type": "object",
                        "properties": {
                            "asha_account_number": {"type": "string"},
                            "certification_status": {
                                "type": "string",
                                "enum": ["active", "inactive", "expired"]
                            },
                            "valid_through": {"type": "string"}
                        }
                    }
                },
                {
                    "label": "check_status",
                    "block_type": "conditional",
                    "branch_conditions": [
                        {
                            "criteria": {
                                "criteria_type": "jinja2_template",
                                "expression": "{{ extract_credentials_output.certification_status == 'expired' }}"
                            },
                            "next_block_label": "notify_expired"
                        },
                        {
                            "is_default": True,
                            "next_block_label": "complete"
                        }
                    ]
                },
                {
                    "label": "notify_expired",
                    "block_type": "http_request",
                    "method": "POST",
                    "url": "https://api.example.com/notifications",
                    "body": {
                        "status": "expired",
                        "details": "{{ extract_credentials_output }}"
                    }
                },
                {
                    "label": "complete",
                    "block_type": "wait",
                    "wait_sec": 0
                }
            ]
        }
    }
)

Advanced Configuration

Browser Session Persistence

Share browser state (cookies, login) across all blocks:
workflow = await client.create_workflow(
    json_definition={
        "title": "Multi-Page Scraper",
        "persist_browser_session": True,  # Blocks share browser context
        "workflow_definition": {...}
    }
)

Proxy Configuration

Set default proxy location for all blocks:
workflow = await client.create_workflow(
    json_definition={
        "title": "UK Market Research",
        "proxy_location": "RESIDENTIAL_GB",  # Use UK residential proxy
        "workflow_definition": {...}
    }
)

Webhook Notifications

Receive notifications when any run of this workflow completes:
workflow = await client.create_workflow(
    json_definition={
        "title": "Invoice Downloader",
        "webhook_callback_url": "https://your-server.com/webhook",
        "workflow_definition": {...}
    }
)

Model Override

Override the default AI model for specific workflows:
workflow = await client.create_workflow(
    json_definition={
        "title": "Complex Form Filler",
        "model": {
            "llm_key": "ANTHROPIC_CLAUDE35SONNET",
            "llm_params": {"temperature": 0.0}
        },
        "workflow_definition": {...}
    }
)

API Response

When you create a workflow, the response contains:
{
  "workflow_permanent_id": "wpid_123456789",
  "workflow_id": "wf_987654321",
  "version": 1,
  "title": "Invoice Downloader",
  "description": "Download invoices from vendor portal",
  "workflow_definition": {
    "parameters": [...],
    "blocks": [...]
  },
  "created_at": "2026-03-02T12:00:00.000000",
  "modified_at": "2026-03-02T12:00:00.000000"
}
Important fields:
  • workflow_permanent_id: Use this to run the workflow
  • workflow_id: The current version ID
  • version: Version number (increments when you update the workflow)

Validation Errors

Skyvern validates workflow definitions on creation. Common errors:

Duplicate Block Labels

Block labels must be unique within a workflow.
Found duplicate label(s): extract_data
Fix: Ensure every block has a unique label.

Invalid Parameter Keys

Key 'user-name' is not a valid parameter name.
Parameter keys must be valid Python identifiers.
Fix: Use only letters, numbers, and underscores. Don’t start with a number.

Missing Required Fields

Field required: navigation_goal
Fix: Each block type has required fields. See Workflow Blocks for details.

Invalid Template References

Parameter 'invalid_param' referenced but not defined
Fix: Ensure all {{ parameter }} references match defined parameter keys.

Best Practices

1. Start Simple

Begin with a minimal workflow and iterate:
# Start with one block
workflow = await client.create_workflow(
    json_definition={
        "title": "Test Navigation",
        "workflow_definition": {
            "parameters": [
                {"key": "url", "parameter_type": "workflow", "workflow_parameter_type": "string"}
            ],
            "blocks": [
                {
                    "label": "navigate",
                    "block_type": "navigation",
                    "url": "{{ url }}",
                    "navigation_goal": "Extract the page title"
                }
            ]
        }
    }
)

# Test it
run = await client.run_workflow(
    workflow_id=workflow.workflow_permanent_id,
    parameters={"url": "https://example.com"}
)

# Once it works, add more blocks

2. Use Descriptive Names

# Bad
parameters:
  - key: p1
    parameter_type: workflow
    workflow_parameter_type: string

blocks:
  - label: step1
    block_type: navigation

# Good
parameters:
  - key: vendor_portal_url
    parameter_type: workflow
    workflow_parameter_type: string

blocks:
  - label: login_to_vendor_portal
    block_type: login

3. Add Descriptions

workflow = await client.create_workflow(
    json_definition={
        "title": "Invoice Downloader",
        "description": "Downloads all invoices from vendor portal since a given date and uploads to S3",
        "workflow_definition": {
            "parameters": [
                {
                    "key": "start_date",
                    "parameter_type": "workflow",
                    "workflow_parameter_type": "string",
                    "description": "Download invoices from this date forward (format: YYYY-MM-DD)"
                }
            ],
            "blocks": [...]
        }
    }
)

4. Version Control

Store workflow definitions in version control:
workflows/
  invoice_downloader.yaml
  job_application.yaml
  credential_verification.yaml
Load and deploy:
import os
import yaml

for file in os.listdir("workflows/"):
    with open(f"workflows/{file}") as f:
        workflow = await client.create_workflow(yaml_definition=f.read())
        print(f"Deployed {workflow.title}: {workflow.workflow_permanent_id}")

Next Steps

Workflow Blocks

Complete reference of all 23 block types

Parameters & Context

Learn how to use parameters and pass data between blocks

Running Workflows

Execute workflows and retrieve results

Build docs developers (and LLMs) love