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:- JSON format via
json_definition(structured object) - YAML format via
yaml_definition(string)
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"
}
workflow_permanent_id: Use this to run the workflowworkflow_id: The current version IDversion: 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
label.
Invalid Parameter Keys
Key 'user-name' is not a valid parameter name.
Parameter keys must be valid Python identifiers.
Missing Required Fields
Field required: navigation_goal
Invalid Template References
Parameter 'invalid_param' referenced but not defined
{{ 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
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