Skip to main content

Install and initialize

npm install @skyvern/client
import { Skyvern } from "@skyvern/client";

const skyvern = new Skyvern({ apiKey: "YOUR_API_KEY" });

const result = await skyvern.runTask({
  body: {
    prompt: "Get the title of the top post on Hacker News",
    url: "https://news.ycombinator.com",
  },
  waitForCompletion: true,
});
console.log(result.output);
Constructor:
new Skyvern({
  apiKey: string,                              // Required
  baseUrl?: string,                            // Override for self-hosted deployments
  environment?: SkyvernEnvironment | string,   // Cloud (default), Staging, or Local
  timeoutInSeconds?: number,                   // HTTP request timeout (default: 60)
  maxRetries?: number,                         // Retry count (default: 2)
  headers?: Record<string, string>,            // Additional headers
})
Environments:
import { SkyvernEnvironment } from "@skyvern/client";
// SkyvernEnvironment.Cloud  → "https://api.skyvern.com"
// SkyvernEnvironment.Staging → "https://api-staging.skyvern.com"
// SkyvernEnvironment.Local  → "http://localhost:8000"
All methods return promises. Requires Node.js 18+.

Imports

import {
  Skyvern,                   // Main client class
  SkyvernEnvironment,        // Cloud, Staging, Local
  SkyvernError,              // Base error class
  SkyvernTimeoutError,       // HTTP timeout error
  SkyvernApi,                // Namespace for API types and error subclasses
} from "@skyvern/client";

// HTTP error subclasses via SkyvernApi namespace:
// SkyvernApi.BadRequestError          — 400
// SkyvernApi.ForbiddenError           — 403
// SkyvernApi.NotFoundError            — 404
// SkyvernApi.ConflictError            — 409
// SkyvernApi.UnprocessableEntityError — 422
Important: SkyvernError and SkyvernTimeoutError are top-level exports. The HTTP-specific errors (BadRequestError, etc.) extend SkyvernError and are accessed via the SkyvernApi namespace.

Tasks

runTask

const result = await skyvern.runTask({
  body: {
    prompt: string,                                  // Required. Natural language instructions.
    url?: string,                                    // Starting page URL.
    engine?: RunEngine,                              // "skyvern_v2" (default), "skyvern_v1", "openai_cua", "anthropic_cua", "ui_tars"
    max_steps?: number,                              // Cap AI steps to limit cost.
    data_extraction_schema?: Record<string, unknown> | string,  // JSON Schema for output.
    browser_session_id?: string,                     // Run in existing session.
    publish_workflow?: boolean,                       // Save generated code as reusable workflow.
    proxy_location?: ProxyLocation,
    webhook_url?: string,
    error_code_mapping?: Record<string, string>,
    totp_identifier?: string,
    totp_url?: string,
    title?: string,
    model?: Record<string, unknown>,
    user_agent?: string,
    extra_http_headers?: Record<string, string>,
    browser_address?: string,
  },
  waitForCompletion?: boolean,                       // Poll until finished. Default: false.
  timeout?: number,                                  // Max wait (seconds). Default: 1800.
}): Promise<TaskRunResponse>
TaskRunResponse fields:
FieldTypeDescription
run_idstringUnique ID (tsk_...).
statusstringcreated | queued | running | completed | failed | terminated | timed_out | canceled
outputRecord<string, unknown> | nullExtracted data. null until completed.
failure_reasonstring | undefinedError description if failed.
downloaded_filesFileInfo[] | undefinedFiles downloaded during the run.
recording_urlstring | undefinedSession recording video URL.
screenshot_urlsstring[] | undefinedFinal screenshots.
app_urlstring | undefinedLink to run in Skyvern UI.
step_countnumber | undefinedNumber of AI steps taken.
created_atstringWhen the run was created.
finished_atstring | undefinedWhen the run finished.

getRun

const run = await skyvern.getRun(runId: string): Promise<GetRunResponse>
Returns status and results for any run (task or workflow).

cancelRun

await skyvern.cancelRun(runId: string): Promise<void>

getRunTimeline

const timeline = await skyvern.getRunTimeline(runId: string): Promise<WorkflowRunTimeline[]>

getRunArtifacts

const artifacts = await skyvern.getRunArtifacts(
  runId: string,
  request?: { artifact_type?: ArtifactType | ArtifactType[] },
): Promise<Artifact[]>

getArtifact

const artifact = await skyvern.getArtifact(artifactId: string): Promise<Artifact>

retryRunWebhook

await skyvern.retryRunWebhook(runId: string): Promise<void>

Workflows

runWorkflow

const result = await skyvern.runWorkflow({
  body: {
    workflow_id: string,                       // Required. Permanent ID (wpid_...).
    parameters?: Record<string, unknown>,      // Input params matching workflow definition.
    browser_session_id?: string,
    browser_profile_id?: string,               // Load saved browser state.
    proxy_location?: ProxyLocation,
    webhook_url?: string,
    title?: string,
    totp_identifier?: string,
    totp_url?: string,
    user_agent?: string,
    extra_http_headers?: Record<string, string>,
    browser_address?: string,
  },
  template?: boolean,
  waitForCompletion?: boolean,                 // Default: false.
  timeout?: number,                            // Default: 1800.
}): Promise<WorkflowRunResponse>
WorkflowRunResponse fields: Same as TaskRunResponse plus run_with, ai_fallback, script_run.

createWorkflow

const workflow = await skyvern.createWorkflow({
  body: { json_definition?: object, yaml_definition?: string },
  folder_id?: string,
}): Promise<Workflow>
Workflow fields: workflow_id, workflow_permanent_id, version, title, workflow_definition, status, created_at

getWorkflows

const workflows = await skyvern.getWorkflows({
  page?: number,
  page_size?: number,
  only_saved_tasks?: boolean,
  only_workflows?: boolean,
  title?: string,
  search_key?: string,
  folder_id?: string,
  status?: WorkflowStatus | WorkflowStatus[],
}): Promise<Workflow[]>

getWorkflow

const workflow = await skyvern.getWorkflow(
  workflowPermanentId: string,
  request?: { version?: number; template?: boolean },
): Promise<Workflow>

getWorkflowVersions

const versions = await skyvern.getWorkflowVersions(
  workflowPermanentId: string,
  request?: { template?: boolean },
): Promise<Workflow[]>

updateWorkflow

const updated = await skyvern.updateWorkflow(
  workflowId: string,              // The permanent ID (wpid_...).
  request?: { json_definition?: object; yaml_definition?: string },
): Promise<Workflow>

deleteWorkflow

await skyvern.deleteWorkflow(workflowId: string): Promise<void>

Browser sessions

A persistent browser instance that stays alive between API calls. Use to chain tasks without losing cookies or login state.

createBrowserSession

const session = await skyvern.createBrowserSession({
  timeout?: number,                            // Minutes (5-1440). Default: 60.
  proxy_location?: ProxyLocation,
  extensions?: Extensions[],                   // "ad-blocker", "captcha-solver"
  browser_type?: PersistentBrowserType,        // "chrome", "msedge"
}): Promise<BrowserSessionResponse>
BrowserSessionResponse fields: browser_session_id, status, browser_address, app_url, timeout, started_at, created_at

getBrowserSession

const session = await skyvern.getBrowserSession(browserSessionId: string): Promise<BrowserSessionResponse>

getBrowserSessions

const sessions = await skyvern.getBrowserSessions(): Promise<BrowserSessionResponse[]>

closeBrowserSession

await skyvern.closeBrowserSession(browserSessionId: string): Promise<void>

Session chaining example

const session = await skyvern.createBrowserSession({});

// Step 1: Log in
await skyvern.runTask({
  body: {
    prompt: "Log in with username demo@example.com",
    url: "https://app.example.com/login",
    browser_session_id: session.browser_session_id,
  },
  waitForCompletion: true,
});

// Step 2: Extract data (same browser, already logged in)
const result = await skyvern.runTask({
  body: {
    prompt: "Go to the invoices page and extract all invoice numbers",
    browser_session_id: session.browser_session_id,
  },
  waitForCompletion: true,
});
console.log(result.output);

// Clean up
await skyvern.closeBrowserSession(session.browser_session_id);

Browser profiles

A saved snapshot of browser state (cookies, local storage). Create from a completed workflow run, then reuse to skip login.

createBrowserProfile

const profile = await skyvern.createBrowserProfile({
  name: string,
  description?: string,
  workflow_run_id?: string,        // Run must have used persist_browser_session: true.
  browser_session_id?: string,     // One of workflow_run_id or browser_session_id is required.
}): Promise<BrowserProfile>
BrowserProfile fields: browser_profile_id, name, description, created_at

listBrowserProfiles

const profiles = await skyvern.listBrowserProfiles({
  include_deleted?: boolean,
}): Promise<BrowserProfile[]>

getBrowserProfile

const profile = await skyvern.getBrowserProfile(profileId: string): Promise<BrowserProfile>

deleteBrowserProfile

await skyvern.deleteBrowserProfile(profileId: string): Promise<void>

Credentials

Store login information securely. Reference by ID instead of passing secrets in code.

createCredential

const credential = await skyvern.createCredential({
  name: string,
  credential_type: CredentialType,       // e.g. "password"
  credential: object,                    // Password: { username: "...", password: "..." }
}): Promise<CredentialResponse>

getCredentials

const creds = await skyvern.getCredentials({
  page?: number,
  page_size?: number,
}): Promise<CredentialResponse[]>

getCredential

const cred = await skyvern.getCredential(credentialId: string): Promise<CredentialResponse>

deleteCredential

await skyvern.deleteCredential(credentialId: string): Promise<void>

sendTotpCode

Send a TOTP code to Skyvern during a run that requires 2FA.
await skyvern.sendTotpCode({
  totp_identifier: string,
  content: string,                       // The TOTP code value.
  task_id?: string,
  workflow_id?: string,
  workflow_run_id?: string,
  source?: string,
  expired_at?: string,
}): Promise<TotpCode>

Helper methods

login

Automate logging into a website using stored credentials.
const result = await skyvern.login({
  credential_type: CredentialType,         // Required. "skyvern", "bitwarden", "1password", "azure_vault".
  url?: string,
  credential_id?: string,                  // When using "skyvern" type.
  prompt?: string,
  browser_session_id?: string,
  browser_address?: string,
  proxy_location?: ProxyLocation,
  webhook_url?: string,
  totp_identifier?: string,
  totp_url?: string,
  extra_http_headers?: Record<string, string>,
  waitForCompletion?: boolean,             // Default: false.
  timeout?: number,                        // Default: 1800.
  // Bitwarden: bitwarden_collection_id, bitwarden_item_id
  // 1Password: onepassword_vault_id, onepassword_item_id
  // Azure: azure_vault_name, azure_vault_username_key, azure_vault_password_key, azure_vault_totp_secret_key
}): Promise<WorkflowRunResponse>

downloadFiles

const result = await skyvern.downloadFiles({
  navigation_goal: string,                 // Required. What to download.
  url?: string,
  browser_session_id?: string,
  browser_profile_id?: string,
  download_suffix?: string,                // Expected extension, e.g. ".pdf"
  download_timeout?: number,
  max_steps_per_run?: number,
  extra_http_headers?: Record<string, string>,
  waitForCompletion?: boolean,             // Default: false.
  timeout?: number,                        // Default: 1800.
}): Promise<WorkflowRunResponse>

uploadFile

const upload = await skyvern.uploadFile({ file }): Promise<UploadFileResponse>
// Returns: { s3_uri: string, presigned_url: string }

Browser automation (Playwright + AI)

The TypeScript SDK extends Playwright with AI capabilities via cloud browsers.

Launch browsers

// Launch new cloud browser
const browser = await skyvern.launchCloudBrowser({ timeout?: number, proxyLocation?: ProxyLocation });

// Reuse existing or create new
const browser = await skyvern.useCloudBrowser({ timeout?: number, proxyLocation?: ProxyLocation });

// Connect to existing cloud session
const browser = await skyvern.connectToCloudBrowserSession(browserSessionId: string);

// Connect via CDP
const browser = await skyvern.connectToBrowserOverCdp(cdpUrl: string);

Get pages

const page = await browser.getWorkingPage();  // Get or create page
const page = await browser.newPage();          // Always create new page
await browser.close();                         // Close browser and session

AI-enhanced page methods

// Click with AI fallback
await page.click("#selector");                                    // Standard Playwright
await page.click({ prompt: "Click the submit button" });          // AI-powered
await page.click("#selector", { prompt: "Click submit" });        // Selector + AI fallback

// Fill with AI fallback
await page.fill("#email", "user@example.com");                    // Standard Playwright
await page.fill({ prompt: "Fill email with user@example.com" });  // AI-powered
await page.fill("#email", "user@example.com", { prompt: "..." }); // Selector + AI fallback

// Select with AI fallback
await page.selectOption("#country", "us");                                     // Standard Playwright
await page.selectOption({ prompt: "Select United States from country" });      // AI-powered

// Single AI actions
await page.act("Click the login button");                         // Perform an action
const data = await page.extract({ prompt: "Extract products" });  // Extract data
const ok = await page.validate("User is logged in");              // Validate state (boolean)
const resp = await page.prompt("What is the heading?", schema);   // LLM prompt

Full task execution via page.agent

await page.agent.runTask("Fill out the form", { timeout: 300 });
await page.agent.login("skyvern", { credentialId: "cred_123" });
await page.agent.downloadFiles("Download the PDF", { downloadSuffix: ".pdf" });
await page.agent.runWorkflow("wpid_abc", { parameters: { key: "value" } });

Error handling

import { SkyvernError, SkyvernTimeoutError, SkyvernApi } from "@skyvern/client";

try {
  const run = await skyvern.getRun("tsk_nonexistent");
} catch (e) {
  if (e instanceof SkyvernApi.NotFoundError) {
    console.log(e.statusCode, e.body);    // 404
  } else if (e instanceof SkyvernTimeoutError) {
    console.log("Request timed out");
  } else if (e instanceof SkyvernError) {
    console.log(e.statusCode, e.body);    // Any other HTTP error
  }
}
Error types: SkyvernApi.BadRequestError (400), SkyvernApi.ForbiddenError (403), SkyvernApi.NotFoundError (404), SkyvernApi.ConflictError (409), SkyvernApi.UnprocessableEntityError (422). All inherit from SkyvernError. Completion timeout throws a standard Error:
try {
  const result = await skyvern.runTask({
    body: { prompt: "...", url: "..." },
    waitForCompletion: true,
    timeout: 300,
  });
} catch (e) {
  if (e instanceof Error && e.message.includes("Timeout")) {
    console.log("Task didn't complete in time");
  }
}
Run failure is not an exception — check result.status:
if (result.status === "failed") console.log(result.failure_reason);
if (result.status === "completed") console.log(result.output);

Request options

Override timeout, retries, or headers per-request (second parameter on all methods):
const result = await skyvern.runTask(
  { body: { prompt: "...", url: "..." } },
  {
    timeoutInSeconds: 120,
    maxRetries: 3,
    headers: { "x-custom-header": "value" },
    abortSignal: controller.signal,
  },
);

Polling pattern

When not using waitForCompletion:
const task = await skyvern.runTask({ body: { prompt: "...", url: "..." } });

const terminal = ["completed", "failed", "terminated", "timed_out", "canceled"];
let run;
while (true) {
  run = await skyvern.getRun(task.run_id);
  if (terminal.includes(run.status)) break;
  await new Promise((resolve) => setTimeout(resolve, 5000));
}
console.log(run.output);

Key constraints

  • browser_profile_id works with runWorkflow only — silently ignored by runTask.
  • Only workflow runs with persist_browser_session: true produce archives for profile creation.
  • Session archiving is async — profile creation may need a short retry delay after a run completes.
  • engine accepts string values: "skyvern_v1", "skyvern_v2", "openai_cua", "anthropic_cua", "ui_tars".
  • Cloud browser methods (launchCloudBrowser, useCloudBrowser, connectToCloudBrowserSession) only work with Cloud or Staging environments.
  • The SDK depends on playwright for browser automation features.