cloud ui docs + cookbooks (#4759)
Co-authored-by: Ritik Sahni <ritiksahni0203@gmail.com> Co-authored-by: Kunal Mishra <kunalm2345@gmail.com>
This commit is contained in:
1057
docs/cookbooks/bulk-invoice-downloader.mdx
Normal file
1057
docs/cookbooks/bulk-invoice-downloader.mdx
Normal file
File diff suppressed because it is too large
Load Diff
1099
docs/cookbooks/healthcare-portal-data.mdx
Normal file
1099
docs/cookbooks/healthcare-portal-data.mdx
Normal file
File diff suppressed because it is too large
Load Diff
975
docs/cookbooks/job-application-filler.mdx
Normal file
975
docs/cookbooks/job-application-filler.mdx
Normal file
@@ -0,0 +1,975 @@
|
||||
---
|
||||
title: Job Applications Pipeline
|
||||
subtitle: Search for jobs and automatically apply using your resume
|
||||
slug: cookbooks/job-application-filler
|
||||
---
|
||||
|
||||
Automate job applications across multiple postings on any job portal.
|
||||
|
||||
This cookbook creates a workflow that logs into a job portal, searches for relevant positions based on your resume, extracts job listings, and applies to each one with AI-generated responses tailored to the role.
|
||||
|
||||
---
|
||||
|
||||
## What you'll build
|
||||
|
||||
A workflow that:
|
||||
|
||||
1. Parses your resume PDF to extract structured information
|
||||
2. Logs into a job portal using saved credentials
|
||||
3. Searches for relevant jobs based on your resume
|
||||
4. Extracts a list of matching job postings
|
||||
5. For each job: extracts details, generates tailored answers, and submits the application
|
||||
|
||||
---
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- **Skyvern Cloud API key** — Get one at [app.skyvern.com/settings](https://app.skyvern.com/settings) → API Keys
|
||||
|
||||
Install the SDK:
|
||||
|
||||
<CodeGroup>
|
||||
```bash Python
|
||||
pip install skyvern
|
||||
```
|
||||
|
||||
```bash TypeScript
|
||||
npm install @skyvern/client
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
Set your API key:
|
||||
|
||||
```bash
|
||||
export SKYVERN_API_KEY="your-api-key"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Sample Job Portal
|
||||
|
||||
We'll use *Job Stash*, a fake job board website created for agent automation testing.
|
||||
Change `job_portal_url` to use your job portal's URL.
|
||||
|
||||
| Field | Value |
|
||||
| -------------- | ---------------------------- |
|
||||
| URL | https://job-stash.vercel.app |
|
||||
| Login email | demo@manicule.dev |
|
||||
| Login password | helloworld |
|
||||
|
||||
|
||||
---
|
||||
|
||||
## Step 1: Store credentials
|
||||
|
||||
Before defining the workflow, store the login email and password Skyvern will use.
|
||||
|
||||
This makes sure your passwords are never stored in the shareable workflow definition and never sent to LLMs.
|
||||
|
||||
<CodeGroup>
|
||||
```python Python
|
||||
import os
|
||||
import asyncio
|
||||
from skyvern import Skyvern
|
||||
|
||||
async def main():
|
||||
client = Skyvern(api_key=os.getenv("SKYVERN_API_KEY"))
|
||||
|
||||
credential = await client.create_credential(
|
||||
name="Job Portal",
|
||||
credential_type="password",
|
||||
credential={
|
||||
"username": "demo@manicule.dev",
|
||||
"password": "helloworld"
|
||||
}
|
||||
)
|
||||
|
||||
print(f"Credential ID: {credential.credential_id}")
|
||||
# Save this ID for your workflow: cred_xxx
|
||||
|
||||
asyncio.run(main())
|
||||
```
|
||||
|
||||
```typescript TypeScript
|
||||
import { SkyvernClient } from "@skyvern/client";
|
||||
|
||||
const client = new SkyvernClient({
|
||||
apiKey: process.env.SKYVERN_API_KEY,
|
||||
});
|
||||
|
||||
const credential = await client.createCredential({
|
||||
name: "Job Portal",
|
||||
credential_type: "password",
|
||||
credential: {
|
||||
username: "demo@manicule.dev",
|
||||
password: "helloworld",
|
||||
},
|
||||
});
|
||||
|
||||
console.log(`Credential ID: ${credential.credential_id}`);
|
||||
```
|
||||
|
||||
```bash cURL
|
||||
curl -X POST "https://api.skyvern.com/v1/credentials" \
|
||||
-H "x-api-key: $SKYVERN_API_KEY" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"name": "Job Portal",
|
||||
"credential_type": "password",
|
||||
"credential": {
|
||||
"username": "demo@manicule.dev",
|
||||
"password": "helloworld"
|
||||
}
|
||||
}'
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
---
|
||||
|
||||
## Step 2: Define workflow parameters
|
||||
|
||||
Parameters are the inputs your workflow accepts. Defining them upfront lets you run the same workflow against different job portals.
|
||||
|
||||
This workflow uses the following parameters:
|
||||
|
||||
- **`resume`** — URL to your resume PDF.
|
||||
- **`credentials`** — The ID of the stored credential to use for login.
|
||||
- **`job_portal_url`** — The job portal's login URL.
|
||||
|
||||
<CodeGroup>
|
||||
```json JSON
|
||||
{
|
||||
"parameters": [
|
||||
{
|
||||
"key": "resume",
|
||||
"parameter_type": "workflow",
|
||||
"workflow_parameter_type": "file_url"
|
||||
},
|
||||
{
|
||||
"key": "credentials",
|
||||
"parameter_type": "workflow",
|
||||
"workflow_parameter_type": "credential_id",
|
||||
"default_value": "your-credential-id"
|
||||
},
|
||||
{
|
||||
"key": "job_portal_url",
|
||||
"description": "URL of the job portal",
|
||||
"parameter_type": "workflow",
|
||||
"workflow_parameter_type": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
```yaml YAML
|
||||
parameters:
|
||||
- key: resume
|
||||
parameter_type: workflow
|
||||
workflow_parameter_type: file_url
|
||||
- key: credentials
|
||||
parameter_type: workflow
|
||||
workflow_parameter_type: credential_id
|
||||
default_value: your-credential-id # <-- replace this
|
||||
- key: job_portal_url
|
||||
description: URL of the job portal
|
||||
parameter_type: workflow
|
||||
workflow_parameter_type: string
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
---
|
||||
|
||||
## Step 3: Create the workflow definition
|
||||
|
||||
The workflow chains together several blocks to automate the full job application process:
|
||||
|
||||
1. **PDF Parser block** — Extracts structured data from your resume
|
||||
2. **Login block** — Authenticates to the job portal using stored credentials
|
||||
3. **Navigation block** — Searches for relevant jobs based on your resume
|
||||
4. **Extraction block** — Extracts a list of job postings from search results
|
||||
5. **For loop** (Go to URL + Extraction + Action + Extraction + Text Prompt + Navigation) — Iterates over each job to extract details, generates tailored answers using an LLM, and submits the application.
|
||||
|
||||
We will add these blocks one by one in the **workflow definition**, a YAML/JSON file that contains the complete description of your workflow logic
|
||||
|
||||
### Workflow Definition format
|
||||
|
||||
<CodeGroup>
|
||||
```json JSON
|
||||
{
|
||||
"title": "Search and Apply for Jobs Workflow",
|
||||
"description": "Search for jobs on any portal, extract listings, generate tailored answers with AI, and submit applications automatically.",
|
||||
"proxy_location": "RESIDENTIAL",
|
||||
"workflow_definition": {
|
||||
"version": 1,
|
||||
"parameters": [],
|
||||
"blocks": []
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```yaml YAML
|
||||
title: Search and Apply for Jobs Workflow
|
||||
description: "Search for jobs on any portal, extract listings, generate tailored answers with AI, and submit applications automatically."
|
||||
proxy_location: RESIDENTIAL # <-- defaults to RESIDENTIAL
|
||||
workflow_definition:
|
||||
version: 1 # <-- auto-increments when you make changes
|
||||
parameters:
|
||||
... # <-- defined in Step 2
|
||||
|
||||
blocks:
|
||||
... # <-- defined one by one in the next steps
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
### PDF Parser block
|
||||
|
||||
The `pdf_parser` block extracts structured information from your resume PDF. Skyvern uses AI to identify your name, contact info, work experience, education, and skills.
|
||||
|
||||
<CodeGroup>
|
||||
```json JSON
|
||||
{
|
||||
"block_type": "pdf_parser",
|
||||
"label": "parsed_resume",
|
||||
"file_url": "{{ resume }}"
|
||||
}
|
||||
```
|
||||
|
||||
```yaml YAML
|
||||
- block_type: pdf_parser
|
||||
label: parsed_resume
|
||||
file_url: "{{ resume }}"
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
The parsed data is accessible as `{{ parsed_resume_output }}` in subsequent blocks.
|
||||
|
||||
### Login block
|
||||
|
||||
The `login` block authenticates using stored credentials. Skyvern injects the username/password directly into form fields without exposing them to the LLM.
|
||||
|
||||
<CodeGroup>
|
||||
```json JSON
|
||||
{
|
||||
"block_type": "login",
|
||||
"label": "login_to_portal",
|
||||
"url": "{{ job_portal_url }}",
|
||||
"title": "login_to_portal",
|
||||
"parameter_keys": ["credentials"],
|
||||
"navigation_goal": "Log in using the provided credentials.\nHandle any cookie consent popups.\nCOMPLETE when on the account dashboard or orders page.",
|
||||
"max_retries": 0,
|
||||
"engine": "skyvern-1.0"
|
||||
}
|
||||
```
|
||||
|
||||
```yaml YAML
|
||||
- block_type: login
|
||||
label: login_to_portal
|
||||
url: "{{ job_portal_url }}"
|
||||
title: login_to_portal
|
||||
parameter_keys:
|
||||
- credentials
|
||||
navigation_goal: |
|
||||
Log in using the provided credentials.
|
||||
Handle any cookie consent popups.
|
||||
COMPLETE when on the account dashboard or orders page.
|
||||
max_retries: 0
|
||||
engine: skyvern-1.0
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
### Search for jobs block
|
||||
|
||||
Navigate to the job search and find relevant positions based on the parsed resume.
|
||||
|
||||
<CodeGroup>
|
||||
```json JSON
|
||||
{
|
||||
"block_type": "navigation",
|
||||
"label": "search_for_job",
|
||||
"url": "",
|
||||
"title": "search_for_job",
|
||||
"engine": "skyvern-1.0",
|
||||
"navigation_goal": "Search for a relevant job based on the parsed resume: {{parsed_resume_output}}",
|
||||
"max_retries": 0
|
||||
}
|
||||
```
|
||||
|
||||
```yaml YAML
|
||||
- block_type: navigation
|
||||
label: search_for_job
|
||||
url: ""
|
||||
title: search_for_job
|
||||
engine: skyvern-1.0
|
||||
navigation_goal: "Search for a relevant job based on the parsed resume: {{parsed_resume_output}}"
|
||||
max_retries: 0
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
### Extract jobs list block
|
||||
|
||||
Extract job postings from the search results. The `data_schema` tells Skyvern exactly what structure to return.
|
||||
|
||||
<CodeGroup>
|
||||
```json JSON
|
||||
{
|
||||
"block_type": "extraction",
|
||||
"label": "jobs_list_extraction",
|
||||
"url": "",
|
||||
"title": "jobs_list_extraction",
|
||||
"data_extraction_goal": "Extract all visible job profiles: job page url, title, employer, location, and type (full time, part time, etc)",
|
||||
"data_schema": {
|
||||
"jobs": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"job_page_url": {
|
||||
"type": "string",
|
||||
"description": "Link to the apply page for the job"
|
||||
},
|
||||
"title": {
|
||||
"type": "string",
|
||||
"description": "Title of the role offered"
|
||||
},
|
||||
"employer": {
|
||||
"type": "string",
|
||||
"description": "Name of the company posting the job profile"
|
||||
},
|
||||
"location": {
|
||||
"type": "string",
|
||||
"description": "Where is the job role based. City and country,"
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"description": "Type for employment: full-time. part-time, contractual, internship etc."
|
||||
}
|
||||
},
|
||||
"required": ["job_page_url", "title", "employer", "location"]
|
||||
}
|
||||
}
|
||||
},
|
||||
"max_retries": 0,
|
||||
"engine": "skyvern-1.0"
|
||||
}
|
||||
```
|
||||
|
||||
```yaml YAML
|
||||
- block_type: extraction
|
||||
label: jobs_list_extraction
|
||||
url: ""
|
||||
title: jobs_list_extraction
|
||||
data_extraction_goal: "Extract all visible job profiles: job page url, title, employer, location, and type (full time, part time, etc)"
|
||||
data_schema:
|
||||
jobs:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
job_page_url:
|
||||
type: string
|
||||
description: Link to the apply page for the job
|
||||
title:
|
||||
type: string
|
||||
description: Title of the role offered
|
||||
employer:
|
||||
type: string
|
||||
description: Name of the company posting the job profile
|
||||
location:
|
||||
type: string
|
||||
description: Where is the job role based. City and country,
|
||||
type:
|
||||
type: string
|
||||
description: "Type for employment: full-time. part-time, contractual, internship etc."
|
||||
required:
|
||||
- job_page_url
|
||||
- title
|
||||
- employer
|
||||
- location
|
||||
max_retries: 0
|
||||
engine: skyvern-1.0
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
The output is accessible as `{{ jobs_list_extraction_output.jobs }}` in subsequent blocks.
|
||||
|
||||
### Apply to jobs loop
|
||||
|
||||
Iterate over each job posting and complete the application. This loop contains multiple blocks that work together:
|
||||
|
||||
1. **goto_url** — Navigate to the job page
|
||||
2. **extraction** — Extract detailed job information
|
||||
3. **action** — Click the Apply button
|
||||
4. **extraction** — Extract application form questions
|
||||
5. **text_prompt** — Generate tailored answers using AI
|
||||
6. **navigation** — Fill out and submit the application
|
||||
|
||||
<CodeGroup>
|
||||
```json JSON
|
||||
{
|
||||
"block_type": "for_loop",
|
||||
"label": "for_each_job",
|
||||
"loop_variable_reference": "{{jobs_list_extraction_output.jobs}}",
|
||||
"continue_on_failure": true,
|
||||
"next_loop_on_failure": true,
|
||||
"complete_if_empty": true,
|
||||
"loop_blocks": [
|
||||
{
|
||||
"block_type": "goto_url",
|
||||
"label": "go_to_each_job",
|
||||
"url": "{{ current_value.job_page_url }}"
|
||||
},
|
||||
{
|
||||
"block_type": "extraction",
|
||||
"label": "extract_job_detail",
|
||||
"url": "",
|
||||
"title": "extract_job_detail",
|
||||
"data_extraction_goal": "Extract every detail about the job role present on the page",
|
||||
"max_retries": 0,
|
||||
"engine": "skyvern-1.0"
|
||||
},
|
||||
{
|
||||
"block_type": "action",
|
||||
"label": "click_apply",
|
||||
"url": "",
|
||||
"title": "click_apply",
|
||||
"navigation_goal": "Find Apply button and click it.\n\nCOMPLETE when the job application is visible on the screen",
|
||||
"max_retries": 0,
|
||||
"engine": "skyvern-1.0"
|
||||
},
|
||||
{
|
||||
"block_type": "extraction",
|
||||
"label": "extract_questions",
|
||||
"url": "",
|
||||
"title": "extract_questions",
|
||||
"data_extraction_goal": "Extract every question in the job application form as a list",
|
||||
"max_retries": 0,
|
||||
"engine": "skyvern-1.0"
|
||||
},
|
||||
{
|
||||
"block_type": "text_prompt",
|
||||
"label": "answer_form_questions",
|
||||
"prompt": "Given:\nresume: {{resume}}\nparsed resume: {{parsed_resume_output}}\ninformation about the job profile: {{extract_job_detail_output}}\napplication form questions: {{extract_questions_output}}\n\nYou are applying for {{ current_value.title }} at {{ current_value.employer }}.\n\nWrite thoughtful and impressive answers to each question using the information from resume and parsed resume output."
|
||||
},
|
||||
{
|
||||
"block_type": "navigation",
|
||||
"label": "apply_to_job",
|
||||
"url": "",
|
||||
"title": "apply_to_job",
|
||||
"engine": "skyvern-1.0",
|
||||
"navigation_goal": "Given:\n{{answer_form_questions_output}}\n\nFill out all of the form fields, including the optional fields.\n\nIf you dont know the answer to an optional question, leave it blank. If you dont know the answer to a required question such as referral name put N/A or something equivalent.\n\nCOMPLETE when the application form has been successfully submitted.\n\nMore context:\nresume: {{resume}}\nperson_information: {{parsed_resume_output}}",
|
||||
"max_retries": 0
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
```yaml YAML
|
||||
- block_type: for_loop
|
||||
label: for_each_job
|
||||
loop_variable_reference: "{{jobs_list_extraction_output.jobs}}"
|
||||
continue_on_failure: true
|
||||
next_loop_on_failure: true
|
||||
complete_if_empty: true
|
||||
loop_blocks:
|
||||
- block_type: goto_url
|
||||
label: go_to_each_job
|
||||
url: "{{ current_value.job_page_url }}"
|
||||
|
||||
- block_type: extraction
|
||||
label: extract_job_detail
|
||||
url: ""
|
||||
title: extract_job_detail
|
||||
data_extraction_goal: Extract every detail about the job role present on the page
|
||||
max_retries: 0
|
||||
engine: skyvern-1.0
|
||||
|
||||
- block_type: action
|
||||
label: click_apply
|
||||
url: ""
|
||||
title: click_apply
|
||||
navigation_goal: |
|
||||
Find Apply button and click it.
|
||||
|
||||
COMPLETE when the job application is visible on the screen
|
||||
max_retries: 0
|
||||
engine: skyvern-1.0
|
||||
|
||||
- block_type: extraction
|
||||
label: extract_questions
|
||||
url: ""
|
||||
title: extract_questions
|
||||
data_extraction_goal: Extract every question in the job application form as a list
|
||||
max_retries: 0
|
||||
engine: skyvern-1.0
|
||||
|
||||
- block_type: text_prompt
|
||||
label: answer_form_questions
|
||||
prompt: |
|
||||
Given:
|
||||
resume: {{resume}}
|
||||
parsed resume: {{parsed_resume_output}}
|
||||
information about the job profile: {{extract_job_detail_output}}
|
||||
application form questions: {{extract_questions_output}}
|
||||
|
||||
You are applying for {{ current_value.title }} at {{ current_value.employer }}.
|
||||
|
||||
Write thoughtful and impressive answers to each question using the information from resume and parsed resume output.
|
||||
|
||||
- block_type: navigation
|
||||
label: apply_to_job
|
||||
url: ""
|
||||
title: apply_to_job
|
||||
engine: skyvern-1.0
|
||||
navigation_goal: |
|
||||
Given:
|
||||
{{answer_form_questions_output}}
|
||||
|
||||
Fill out all of the form fields, including the optional fields.
|
||||
|
||||
If you dont know the answer to an optional question, leave it blank. If you dont know the answer to a required question such as referral name put N/A or something equivalent.
|
||||
|
||||
COMPLETE when the application form has been successfully submitted.
|
||||
|
||||
More context:
|
||||
resume: {{resume}}
|
||||
person_information: {{parsed_resume_output}}
|
||||
max_retries: 0
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
**Key pattern:** `continue_on_failure: true` ensures that if one application fails, the workflow continues to the next job. Inside the loop, `{{ current_value }}` gives you the current job being processed.
|
||||
|
||||
### Complete workflow definition
|
||||
|
||||
Save this complete definition as `job-application-workflow.yaml` (or `.json`) before running.
|
||||
|
||||
<Accordion title="Complete workflow definition">
|
||||
<CodeGroup>
|
||||
```json JSON
|
||||
{
|
||||
"title": "Search and Apply for Jobs Workflow",
|
||||
"description": "Search for jobs on any portal, extract listings, generate tailored answers with AI, and submit applications automatically.",
|
||||
"proxy_location": "RESIDENTIAL",
|
||||
"workflow_definition": {
|
||||
"version": 1,
|
||||
"parameters": [
|
||||
{ "key": "resume", "parameter_type": "workflow", "workflow_parameter_type": "file_url" },
|
||||
{ "key": "credentials", "parameter_type": "workflow", "workflow_parameter_type": "credential_id", "default_value": "your-credential-id" },
|
||||
{ "key": "job_portal_url", "description": "URL of the job portal", "parameter_type": "workflow", "workflow_parameter_type": "string" }
|
||||
],
|
||||
"blocks": [
|
||||
{
|
||||
"block_type": "pdf_parser",
|
||||
"label": "parsed_resume",
|
||||
"file_url": "{{ resume }}"
|
||||
},
|
||||
{
|
||||
"block_type": "login",
|
||||
"label": "login_to_portal",
|
||||
"url": "{{ job_portal_url }}",
|
||||
"title": "login_to_portal",
|
||||
"parameter_keys": ["credentials"],
|
||||
"navigation_goal": "Log in using the provided credentials.\nHandle any cookie consent popups.\nCOMPLETE when on the account dashboard or orders page.",
|
||||
"max_retries": 0,
|
||||
"engine": "skyvern-1.0"
|
||||
},
|
||||
{
|
||||
"block_type": "navigation",
|
||||
"label": "search_for_job",
|
||||
"url": "",
|
||||
"title": "search_for_job",
|
||||
"engine": "skyvern-1.0",
|
||||
"navigation_goal": "Search for a relevant job based on the parsed resume: {{parsed_resume_output}}",
|
||||
"max_retries": 0
|
||||
},
|
||||
{
|
||||
"block_type": "extraction",
|
||||
"label": "jobs_list_extraction",
|
||||
"url": "",
|
||||
"title": "jobs_list_extraction",
|
||||
"data_extraction_goal": "Extract all visible job profiles: job page url, title, employer, location, and type (full time, part time, etc)",
|
||||
"data_schema": {
|
||||
"jobs": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"job_page_url": { "type": "string", "description": "Link to the apply page for the job" },
|
||||
"title": { "type": "string", "description": "Title of the role offered" },
|
||||
"employer": { "type": "string", "description": "Name of the company posting the job profile" },
|
||||
"location": { "type": "string", "description": "Where is the job role based. City and country," },
|
||||
"type": { "type": "string", "description": "Type for employment: full-time. part-time, contractual, internship etc." }
|
||||
},
|
||||
"required": ["job_page_url", "title", "employer", "location"]
|
||||
}
|
||||
}
|
||||
},
|
||||
"max_retries": 0,
|
||||
"engine": "skyvern-1.0"
|
||||
},
|
||||
{
|
||||
"block_type": "for_loop",
|
||||
"label": "for_each_job",
|
||||
"loop_variable_reference": "{{jobs_list_extraction_output.jobs}}",
|
||||
"continue_on_failure": true,
|
||||
"next_loop_on_failure": true,
|
||||
"complete_if_empty": true,
|
||||
"loop_blocks": [
|
||||
{
|
||||
"block_type": "goto_url",
|
||||
"label": "go_to_each_job",
|
||||
"url": "{{ current_value.job_page_url }}"
|
||||
},
|
||||
{
|
||||
"block_type": "extraction",
|
||||
"label": "extract_job_detail",
|
||||
"url": "",
|
||||
"title": "extract_job_detail",
|
||||
"data_extraction_goal": "Extract every detail about the job role present on the page",
|
||||
"max_retries": 0,
|
||||
"engine": "skyvern-1.0"
|
||||
},
|
||||
{
|
||||
"block_type": "action",
|
||||
"label": "click_apply",
|
||||
"url": "",
|
||||
"title": "click_apply",
|
||||
"navigation_goal": "Find Apply button and click it.\n\nCOMPLETE when the job application is visible on the screen",
|
||||
"max_retries": 0,
|
||||
"engine": "skyvern-1.0"
|
||||
},
|
||||
{
|
||||
"block_type": "extraction",
|
||||
"label": "extract_questions",
|
||||
"url": "",
|
||||
"title": "extract_questions",
|
||||
"data_extraction_goal": "Extract every question in the job application form as a list",
|
||||
"max_retries": 0,
|
||||
"engine": "skyvern-1.0"
|
||||
},
|
||||
{
|
||||
"block_type": "text_prompt",
|
||||
"label": "answer_form_questions",
|
||||
"prompt": "Given:\nresume: {{resume}}\nparsed resume: {{parsed_resume_output}}\ninformation about the job profile: {{extract_job_detail_output}}\napplication form questions: {{extract_questions_output}}\n\nYou are applying for {{ current_value.title }} at {{ current_value.employer }}.\n\nWrite thoughtful and impressive answers to each question using the information from resume and parsed resume output."
|
||||
},
|
||||
{
|
||||
"block_type": "navigation",
|
||||
"label": "apply_to_job",
|
||||
"url": "",
|
||||
"title": "apply_to_job",
|
||||
"engine": "skyvern-1.0",
|
||||
"navigation_goal": "Given:\n{{answer_form_questions_output}}\n\nFill out all of the form fields, including the optional fields.\n\nIf you dont know the answer to an optional question, leave it blank. If you dont know the answer to a required question such as referral name put N/A or something equivalent.\n\nCOMPLETE when the application form has been successfully submitted.\n\nMore context:\nresume: {{resume}}\nperson_information: {{parsed_resume_output}}",
|
||||
"max_retries": 0
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```yaml YAML
|
||||
title: Search and Apply for Jobs Workflow
|
||||
description: "Search for jobs on any portal, extract listings, generate tailored answers with AI, and submit applications automatically."
|
||||
proxy_location: RESIDENTIAL
|
||||
workflow_definition:
|
||||
version: 1
|
||||
parameters:
|
||||
- key: resume
|
||||
parameter_type: workflow
|
||||
workflow_parameter_type: file_url
|
||||
- key: credentials
|
||||
parameter_type: workflow
|
||||
workflow_parameter_type: credential_id
|
||||
default_value: your-credential-id # <-- replace this
|
||||
- key: job_portal_url
|
||||
description: URL of the job portal
|
||||
parameter_type: workflow
|
||||
workflow_parameter_type: string
|
||||
|
||||
blocks:
|
||||
- block_type: pdf_parser
|
||||
label: parsed_resume
|
||||
file_url: "{{ resume }}"
|
||||
|
||||
- block_type: login
|
||||
label: login_to_portal
|
||||
url: "{{ job_portal_url }}"
|
||||
title: login_to_portal
|
||||
parameter_keys:
|
||||
- credentials
|
||||
navigation_goal: |
|
||||
Log in using the provided credentials.
|
||||
Handle any cookie consent popups.
|
||||
COMPLETE when on the account dashboard or orders page.
|
||||
max_retries: 0
|
||||
engine: skyvern-1.0
|
||||
|
||||
- block_type: navigation
|
||||
label: search_for_job
|
||||
url: ""
|
||||
title: search_for_job
|
||||
engine: skyvern-1.0
|
||||
navigation_goal: "Search for a relevant job based on the parsed resume: {{parsed_resume_output}}"
|
||||
max_retries: 0
|
||||
|
||||
- block_type: extraction
|
||||
label: jobs_list_extraction
|
||||
url: ""
|
||||
title: jobs_list_extraction
|
||||
data_extraction_goal: "Extract all visible job profiles: job page url, title, employer, location, and type (full time, part time, etc)"
|
||||
data_schema:
|
||||
jobs:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
job_page_url:
|
||||
type: string
|
||||
description: Link to the apply page for the job
|
||||
title:
|
||||
type: string
|
||||
description: Title of the role offered
|
||||
employer:
|
||||
type: string
|
||||
description: Name of the company posting the job profile
|
||||
location:
|
||||
type: string
|
||||
description: Where is the job role based. City and country,
|
||||
type:
|
||||
type: string
|
||||
description: "Type for employment: full-time. part-time, contractual, internship etc."
|
||||
required:
|
||||
- job_page_url
|
||||
- title
|
||||
- employer
|
||||
- location
|
||||
max_retries: 0
|
||||
engine: skyvern-1.0
|
||||
|
||||
- block_type: for_loop
|
||||
label: for_each_job
|
||||
loop_variable_reference: "{{jobs_list_extraction_output.jobs}}"
|
||||
continue_on_failure: true
|
||||
next_loop_on_failure: true
|
||||
complete_if_empty: true
|
||||
loop_blocks:
|
||||
- block_type: goto_url
|
||||
label: go_to_each_job
|
||||
url: "{{ current_value.job_page_url }}"
|
||||
|
||||
- block_type: extraction
|
||||
label: extract_job_detail
|
||||
url: ""
|
||||
title: extract_job_detail
|
||||
data_extraction_goal: Extract every detail about the job role present on the page
|
||||
max_retries: 0
|
||||
engine: skyvern-1.0
|
||||
|
||||
- block_type: action
|
||||
label: click_apply
|
||||
url: ""
|
||||
title: click_apply
|
||||
navigation_goal: |
|
||||
Find Apply button and click it.
|
||||
|
||||
COMPLETE when the job application is visible on the screen
|
||||
max_retries: 0
|
||||
engine: skyvern-1.0
|
||||
|
||||
- block_type: extraction
|
||||
label: extract_questions
|
||||
url: ""
|
||||
title: extract_questions
|
||||
data_extraction_goal: Extract every question in the job application form as a list
|
||||
max_retries: 0
|
||||
engine: skyvern-1.0
|
||||
|
||||
- block_type: text_prompt
|
||||
label: answer_form_questions
|
||||
prompt: |
|
||||
Given:
|
||||
resume: {{resume}}
|
||||
parsed resume: {{parsed_resume_output}}
|
||||
information about the job profile: {{extract_job_detail_output}}
|
||||
application form questions: {{extract_questions_output}}
|
||||
|
||||
You are applying for {{ current_value.title }} at {{ current_value.employer }}.
|
||||
|
||||
Write thoughtful and impressive answers to each question using the information from resume and parsed resume output.
|
||||
|
||||
- block_type: navigation
|
||||
label: apply_to_job
|
||||
url: ""
|
||||
title: apply_to_job
|
||||
engine: skyvern-1.0
|
||||
navigation_goal: |
|
||||
Given:
|
||||
{{answer_form_questions_output}}
|
||||
|
||||
Fill out all of the form fields, including the optional fields.
|
||||
|
||||
If you dont know the answer to an optional question, leave it blank. If you dont know the answer to a required question such as referral name put N/A or something equivalent.
|
||||
|
||||
COMPLETE when the application form has been successfully submitted.
|
||||
|
||||
More context:
|
||||
resume: {{resume}}
|
||||
person_information: {{parsed_resume_output}}
|
||||
max_retries: 0
|
||||
```
|
||||
</CodeGroup>
|
||||
</Accordion>
|
||||
|
||||
---
|
||||
|
||||
## Step 4: Run the workflow
|
||||
|
||||
Create the workflow from your definition file and execute it using the SDK.
|
||||
|
||||
<CodeGroup>
|
||||
```python Python
|
||||
import os
|
||||
import asyncio
|
||||
from skyvern import Skyvern
|
||||
|
||||
async def main():
|
||||
client = Skyvern(api_key=os.getenv("SKYVERN_API_KEY"))
|
||||
|
||||
# Create workflow from YAML file
|
||||
workflow = await client.create_workflow(
|
||||
yaml_definition=open("job-application-workflow.yaml").read()
|
||||
)
|
||||
print(f"Created workflow: {workflow.workflow_permanent_id}")
|
||||
|
||||
# Run the workflow
|
||||
run = await client.run_workflow(
|
||||
workflow_id=workflow.workflow_permanent_id,
|
||||
parameters={
|
||||
"resume": "https://your-resume-url.com/resume.pdf", # <-- replace this
|
||||
"job_portal_url": "https://job-stash.vercel.app"
|
||||
}
|
||||
)
|
||||
print(f"Started run: {run.run_id}")
|
||||
|
||||
# Poll for completion
|
||||
while True:
|
||||
result = await client.get_run(run.run_id)
|
||||
if result.status in ["completed", "failed", "terminated"]:
|
||||
break
|
||||
print(f"Status: {result.status}")
|
||||
await asyncio.sleep(10)
|
||||
|
||||
print(f"Final status: {result.status}")
|
||||
if result.status == "completed":
|
||||
print("Job applications submitted successfully")
|
||||
|
||||
asyncio.run(main())
|
||||
```
|
||||
|
||||
```typescript TypeScript
|
||||
import { SkyvernClient } from "@skyvern/client";
|
||||
import * as fs from "fs";
|
||||
|
||||
async function main() {
|
||||
const client = new SkyvernClient({
|
||||
apiKey: process.env.SKYVERN_API_KEY,
|
||||
});
|
||||
|
||||
// Create workflow from YAML file
|
||||
const workflow = await client.createWorkflow({
|
||||
body: {
|
||||
yaml_definition: fs.readFileSync("job-application-workflow.yaml", "utf-8"),
|
||||
},
|
||||
});
|
||||
console.log(`Created workflow: ${workflow.workflow_permanent_id}`);
|
||||
|
||||
// Run the workflow
|
||||
const run = await client.runWorkflow({
|
||||
body: {
|
||||
workflow_id: workflow.workflow_permanent_id,
|
||||
parameters: {
|
||||
resume: "https://your-resume-url.com/resume.pdf", // <-- replace this
|
||||
job_portal_url: "https://job-stash.vercel.app",
|
||||
},
|
||||
},
|
||||
});
|
||||
console.log(`Started run: ${run.run_id}`);
|
||||
|
||||
// Poll for completion
|
||||
while (true) {
|
||||
const result = await client.getRun(run.run_id);
|
||||
if (["completed", "failed", "terminated"].includes(result.status)) {
|
||||
console.log(`Final status: ${result.status}`);
|
||||
if (result.status === "completed") {
|
||||
console.log("Job applications submitted successfully");
|
||||
}
|
||||
break;
|
||||
}
|
||||
console.log(`Status: ${result.status}`);
|
||||
await new Promise((r) => setTimeout(r, 10000));
|
||||
}
|
||||
}
|
||||
|
||||
main();
|
||||
```
|
||||
|
||||
```bash cURL
|
||||
# Create workflow
|
||||
WORKFLOW=$(curl -s -X POST "https://api.skyvern.com/v1/workflows" \
|
||||
-H "x-api-key: $SKYVERN_API_KEY" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "{\"yaml_definition\": $(cat job-application-workflow.yaml | jq -Rs .)}")
|
||||
|
||||
WORKFLOW_ID=$(echo "$WORKFLOW" | jq -r '.workflow_permanent_id')
|
||||
echo "Created workflow: $WORKFLOW_ID"
|
||||
|
||||
# Run workflow (replace resume URL with your own)
|
||||
RUN=$(curl -s -X POST "https://api.skyvern.com/v1/run/workflows" \
|
||||
-H "x-api-key: $SKYVERN_API_KEY" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "{
|
||||
\"workflow_id\": \"$WORKFLOW_ID\",
|
||||
\"parameters\": {
|
||||
\"resume\": \"https://your-resume-url.com/resume.pdf\",
|
||||
\"job_portal_url\": \"https://job-stash.vercel.app\"
|
||||
}
|
||||
}")
|
||||
|
||||
RUN_ID=$(echo "$RUN" | jq -r '.run_id')
|
||||
echo "Started run: $RUN_ID"
|
||||
|
||||
# Poll for completion
|
||||
while true; do
|
||||
RESULT=$(curl -s "https://api.skyvern.com/v1/runs/$RUN_ID" \
|
||||
-H "x-api-key: $SKYVERN_API_KEY")
|
||||
STATUS=$(echo "$RESULT" | jq -r '.status')
|
||||
echo "Status: $STATUS"
|
||||
|
||||
if [[ "$STATUS" == "completed" || "$STATUS" == "failed" || "$STATUS" == "terminated" ]]; then
|
||||
echo "Workflow finished with status: $STATUS"
|
||||
break
|
||||
fi
|
||||
sleep 10
|
||||
done
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
---
|
||||
|
||||
## Resources
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Workflow Blocks Reference" icon="cube" href="/multi-step-automations/workflow-blocks-reference">
|
||||
Complete parameter reference for all block types
|
||||
</Card>
|
||||
<Card title="Credential Management" icon="key" href="/sdk-reference/credentials">
|
||||
Securely store and use login credentials
|
||||
</Card>
|
||||
<Card title="File Operations" icon="file" href="/multi-step-automations/file-operations">
|
||||
Upload and parse files in workflows
|
||||
</Card>
|
||||
<Card title="Error Handling" icon="triangle-exclamation" href="/going-to-production/error-handling">
|
||||
Handle failures and retries in production
|
||||
</Card>
|
||||
</CardGroup>
|
||||
81
docs/cookbooks/overview.mdx
Normal file
81
docs/cookbooks/overview.mdx
Normal file
@@ -0,0 +1,81 @@
|
||||
---
|
||||
title: Overview
|
||||
subtitle: End-to-end workflow examples for common automation scenarios
|
||||
slug: cookbooks/overview
|
||||
---
|
||||
|
||||
Cookbooks are complete, production-ready workflow examples that demonstrate how to use Skyvern Browser Automation agent in real-world use-cases.
|
||||
|
||||
Each cookbook walks through the workflow step-by-step, explaining design decisions and showing you how to adapt it for your use case.
|
||||
|
||||
---
|
||||
|
||||
## Available cookbooks
|
||||
|
||||
<CardGroup cols={1}>
|
||||
<Card
|
||||
title="Bulk Invoice Downloader"
|
||||
icon="file-invoice-dollar"
|
||||
href="/cookbooks/bulk-invoice-downloader"
|
||||
>
|
||||
Download invoices from vendor portals, parse PDFs to extract amounts, and email a summary with attachments.
|
||||
</Card>
|
||||
<Card
|
||||
title="Job Applications Pipeline"
|
||||
icon="briefcase"
|
||||
href="/cookbooks/job-application-filler"
|
||||
>
|
||||
Search for jobs on any portal, extract listings, generate tailored answers with AI, and submit applications automatically.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
---
|
||||
|
||||
## Getting your API key
|
||||
|
||||
All cookbooks require a Skyvern API key. Here's how to get one:
|
||||
|
||||
<Steps>
|
||||
<Step title="Create an account">
|
||||
Sign up at [app.skyvern.com](https://app.skyvern.com) or log in to your existing account.
|
||||
</Step>
|
||||
<Step title="Go to Settings">
|
||||
Click **Settings** in the left sidebar, then copy your **API Keys**.
|
||||
<Frame>
|
||||
<img src="/images/get-api-key.png" alt="API Keys settings page" />
|
||||
</Frame>
|
||||
</Step>
|
||||
<Step title="Set your environment variable">
|
||||
Set the `SKYVERN_API_KEY` environment variable in your shell or `.env` file.
|
||||
```bash
|
||||
export SKYVERN_API_KEY="your-api-key"
|
||||
```
|
||||
Or add it to your `.env` file:
|
||||
```bash
|
||||
SKYVERN_API_KEY=your-api-key
|
||||
```
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
---
|
||||
|
||||
## Other Resources
|
||||
|
||||
If you're new to Skyvern workflows, start with these foundational guides:
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card
|
||||
title="Build a Workflow"
|
||||
icon="hammer"
|
||||
href="/multi-step-automations/build-a-workflow"
|
||||
>
|
||||
Learn workflow basics: parameters, blocks, and data passing
|
||||
</Card>
|
||||
<Card
|
||||
title="Workflow Blocks Reference"
|
||||
icon="cube"
|
||||
href="/multi-step-automations/workflow-blocks-reference"
|
||||
>
|
||||
Complete reference for all block types
|
||||
</Card>
|
||||
</CardGroup>
|
||||
Reference in New Issue
Block a user