diff --git a/docs/docs.json b/docs/docs.json
index bbb0664b..53dff4c4 100644
--- a/docs/docs.json
+++ b/docs/docs.json
@@ -118,7 +118,7 @@
]
},
{
- "tab": "Python SDK",
+ "tab": "SDKs",
"groups": [
{
"group": "Python SDK",
@@ -133,6 +133,21 @@
"sdk-reference/error-handling",
"sdk-reference/complete-reference"
]
+ },
+ {
+ "group": "TypeScript SDK",
+ "pages": [
+ "ts-sdk-reference/overview",
+ "ts-sdk-reference/tasks",
+ "ts-sdk-reference/workflows",
+ "ts-sdk-reference/browser-sessions",
+ "ts-sdk-reference/browser-profiles",
+ "ts-sdk-reference/credentials",
+ "ts-sdk-reference/helpers",
+ "ts-sdk-reference/browser-automation",
+ "ts-sdk-reference/error-handling",
+ "ts-sdk-reference/complete-reference"
+ ]
}
]
},
diff --git a/docs/ts-sdk-reference/browser-automation.mdx b/docs/ts-sdk-reference/browser-automation.mdx
new file mode 100644
index 00000000..23c75078
--- /dev/null
+++ b/docs/ts-sdk-reference/browser-automation.mdx
@@ -0,0 +1,403 @@
+---
+title: Browser Automation
+subtitle: Control cloud browsers with Playwright + AI
+slug: ts-sdk-reference/browser-automation
+---
+
+The TypeScript SDK extends Playwright with AI-powered browser automation. Launch a cloud browser, get a page that works like a normal Playwright `Page`, then use `.agent` for full-task AI execution or AI-enhanced versions of `click`, `fill`, and `selectOption` that fall back to natural language when selectors fail.
+
+---
+
+## Launch a cloud browser
+
+### `launchCloudBrowser`
+
+Create a new cloud-hosted browser session and connect to it.
+
+```typescript
+const browser = await skyvern.launchCloudBrowser();
+const page = await browser.getWorkingPage();
+
+// Use Playwright methods
+await page.goto("https://example.com");
+
+// Or use AI
+await page.agent.runTask("Fill out the contact form and submit it");
+
+await browser.close();
+```
+
+#### Parameters
+
+| Parameter | Type | Required | Default | Description |
+|-----------|------|----------|---------|-------------|
+| `timeout` | `number` | No | `60` | Session timeout in minutes (5–1440). |
+| `proxyLocation` | `ProxyLocation` | No | `undefined` | Geographic proxy location for browser traffic. |
+
+#### Returns `SkyvernBrowser`
+
+
+Cloud browser sessions are only available with `SkyvernEnvironment.Cloud` or `SkyvernEnvironment.Staging`.
+
+
+---
+
+### `useCloudBrowser`
+
+Get or create a cloud browser session. Reuses the most recent available session if one exists, otherwise creates a new one.
+
+```typescript
+const browser = await skyvern.useCloudBrowser();
+const page = await browser.getWorkingPage();
+```
+
+#### Parameters
+
+Same as `launchCloudBrowser`. Options are only used when creating a new session.
+
+#### Returns `SkyvernBrowser`
+
+---
+
+### `connectToCloudBrowserSession`
+
+Connect to an existing cloud browser session by ID.
+
+```typescript
+const browser = await skyvern.connectToCloudBrowserSession("pbs_abc123");
+const page = await browser.getWorkingPage();
+```
+
+#### Parameters
+
+| Parameter | Type | Required | Description |
+|-----------|------|----------|-------------|
+| `browserSessionId` | `string` | Yes | The ID of the cloud browser session. |
+
+#### Returns `SkyvernBrowser`
+
+---
+
+### `connectToBrowserOverCdp`
+
+Connect to any browser running with Chrome DevTools Protocol (CDP) enabled, whether local or remote.
+
+```typescript
+const browser = await skyvern.connectToBrowserOverCdp("http://localhost:9222");
+const page = await browser.getWorkingPage();
+```
+
+#### Parameters
+
+| Parameter | Type | Required | Description |
+|-----------|------|----------|-------------|
+| `cdpUrl` | `string` | Yes | The CDP WebSocket URL (e.g., `"http://localhost:9222"`). |
+
+#### Returns `SkyvernBrowser`
+
+---
+
+## SkyvernBrowser
+
+A browser context wrapper that provides Skyvern-enabled pages.
+
+### `getWorkingPage()`
+
+Get the most recent page or create a new one if none exists.
+
+```typescript
+const page = await browser.getWorkingPage();
+```
+
+### `newPage()`
+
+Create a new page (tab) in the browser context.
+
+```typescript
+const page = await browser.newPage();
+```
+
+### `close()`
+
+Close the browser and release resources. If connected to a cloud session, also closes the session.
+
+```typescript
+await browser.close();
+```
+
+---
+
+## SkyvernBrowserPage
+
+A `SkyvernBrowserPage` extends Playwright's `Page` with AI capabilities. Every standard Playwright method (`goto`, `click`, `fill`, `waitForSelector`, etc.) works as-is. Additionally, it provides:
+
+- **`page.agent`** — AI-powered task execution (full automations)
+- **AI-enhanced `click`, `fill`, `selectOption`** — try selectors first, fall back to AI
+- **`page.act`, `page.extract`, `page.validate`, `page.prompt`** — single AI actions
+
+---
+
+### AI-enhanced Playwright methods
+
+These methods extend the standard Playwright API with AI fallback. Pass a CSS selector and it works like normal Playwright. Add a `prompt` option and if the selector fails, AI takes over.
+
+#### `click`
+
+```typescript
+// Standard Playwright click
+await page.click("#submit-button");
+
+// AI-powered click (no selector needed)
+await page.click({ prompt: "Click the 'Submit' button" });
+
+// Selector with AI fallback
+await page.click("#submit-button", { prompt: "Click the 'Submit' button" });
+```
+
+#### `fill`
+
+```typescript
+// Standard Playwright fill
+await page.fill("#email", "user@example.com");
+
+// AI-powered fill
+await page.fill({ prompt: "Fill 'user@example.com' in the email field" });
+
+// Selector with AI fallback
+await page.fill("#email", "user@example.com", {
+ prompt: "Fill the email address field",
+});
+```
+
+#### `selectOption`
+
+```typescript
+// Standard Playwright select
+await page.selectOption("#country", "us");
+
+// AI-powered select
+await page.selectOption({ prompt: "Select 'United States' from the country dropdown" });
+
+// Selector with AI fallback
+await page.selectOption("#country", "us", {
+ prompt: "Select United States from country",
+});
+```
+
+---
+
+### `page.agent` — Full task execution
+
+The `agent` property provides methods for running complete AI-powered automations within the browser page context. These always wait for completion.
+
+#### `agent.runTask`
+
+Run a complete AI task in the context of the current page.
+
+```typescript
+const result = await page.agent.runTask("Fill out the contact form and submit it", {
+ dataExtractionSchema: {
+ type: "object",
+ properties: {
+ confirmation_number: { type: "string" },
+ },
+ },
+});
+console.log(result.output);
+```
+
+| Parameter | Type | Required | Description |
+|-----------|------|----------|-------------|
+| `prompt` | `string` | Yes | Natural language task description. |
+| `options.engine` | `RunEngine` | No | AI engine to use. |
+| `options.url` | `string` | No | URL to navigate to. Defaults to current page URL. |
+| `options.dataExtractionSchema` | `Record \| string` | No | JSON schema for output. |
+| `options.maxSteps` | `number` | No | Maximum AI steps. |
+| `options.timeout` | `number` | No | Max wait time in seconds. Default: `1800`. |
+| `options.webhookUrl` | `string` | No | Webhook URL for notifications. |
+| `options.totpIdentifier` | `string` | No | TOTP identifier. |
+| `options.totpUrl` | `string` | No | TOTP URL. |
+| `options.title` | `string` | No | Run display name. |
+| `options.errorCodeMapping` | `Record` | No | Custom error code mapping. |
+| `options.model` | `Record` | No | LLM model configuration. |
+
+Returns `TaskRunResponse`.
+
+#### `agent.login`
+
+Run a login workflow in the context of the current page. Supports multiple credential providers via overloaded signatures.
+
+```typescript
+// Skyvern credentials
+await page.agent.login("skyvern", {
+ credentialId: "cred_123",
+});
+
+// Bitwarden
+await page.agent.login("bitwarden", {
+ bitwardenItemId: "item_id",
+ bitwardenCollectionId: "collection_id",
+});
+
+// 1Password
+await page.agent.login("1password", {
+ onepasswordVaultId: "vault_id",
+ onepasswordItemId: "item_id",
+});
+
+// Azure Vault
+await page.agent.login("azure_vault", {
+ azureVaultName: "vault_name",
+ azureVaultUsernameKey: "username_key",
+ azureVaultPasswordKey: "password_key",
+});
+```
+
+Returns `WorkflowRunResponse`.
+
+#### `agent.downloadFiles`
+
+Download files in the context of the current page.
+
+```typescript
+const result = await page.agent.downloadFiles("Download the latest invoice PDF", {
+ downloadSuffix: ".pdf",
+ downloadTimeout: 30,
+});
+```
+
+| Parameter | Type | Required | Description |
+|-----------|------|----------|-------------|
+| `prompt` | `string` | Yes | What to download. |
+| `options.url` | `string` | No | URL to navigate to. Defaults to current page URL. |
+| `options.downloadSuffix` | `string` | No | Expected file extension. |
+| `options.downloadTimeout` | `number` | No | Download timeout in seconds. |
+| `options.maxStepsPerRun` | `number` | No | Max AI steps. |
+| `options.timeout` | `number` | No | Max wait time in seconds. Default: `1800`. |
+
+Returns `WorkflowRunResponse`.
+
+#### `agent.runWorkflow`
+
+Run a pre-defined workflow in the context of the current page.
+
+```typescript
+const result = await page.agent.runWorkflow("wpid_abc123", {
+ parameters: { company_name: "Acme Corp" },
+});
+console.log(result.output);
+```
+
+| Parameter | Type | Required | Description |
+|-----------|------|----------|-------------|
+| `workflowId` | `string` | Yes | The workflow permanent ID. |
+| `options.parameters` | `Record` | No | Workflow input parameters. |
+| `options.template` | `boolean` | No | Whether it's a template. |
+| `options.title` | `string` | No | Run display name. |
+| `options.timeout` | `number` | No | Max wait time in seconds. Default: `1800`. |
+
+Returns `WorkflowRunResponse`.
+
+---
+
+### Single AI actions
+
+#### `act`
+
+Perform a single AI action on the page.
+
+```typescript
+await page.act("Scroll down and click the 'Load More' button");
+```
+
+#### `extract`
+
+Extract structured data from the current page.
+
+```typescript
+const data = await page.extract({
+ prompt: "Extract all product names and prices",
+ schema: {
+ type: "array",
+ items: {
+ type: "object",
+ properties: {
+ name: { type: "string" },
+ price: { type: "string" },
+ },
+ },
+ },
+});
+console.log(data);
+```
+
+| Parameter | Type | Required | Description |
+|-----------|------|----------|-------------|
+| `prompt` | `string` | Yes | What to extract. |
+| `schema` | `Record \| unknown[] \| string` | No | JSON schema for output. |
+| `errorCodeMapping` | `Record` | No | Custom error codes. |
+
+Returns `Record | unknown[] | string | null`.
+
+#### `validate`
+
+Validate the current page state with AI.
+
+```typescript
+const isLoggedIn = await page.validate("Check if the user is logged in");
+console.log(isLoggedIn); // true or false
+```
+
+Returns `boolean`.
+
+#### `prompt`
+
+Send a prompt to the LLM and get a structured response.
+
+```typescript
+const result = await page.prompt(
+ "What is the main heading on this page?",
+ { heading: { type: "string" } },
+);
+console.log(result);
+```
+
+Returns `Record | unknown[] | string | null`.
+
+---
+
+## Complete example
+
+```typescript
+import { Skyvern } from "@skyvern/client";
+
+const skyvern = new Skyvern({ apiKey: "YOUR_API_KEY" });
+const browser = await skyvern.launchCloudBrowser();
+const page = await browser.getWorkingPage();
+
+// Navigate with Playwright
+await page.goto("https://app.example.com");
+
+// Login with AI
+await page.agent.login("skyvern", { credentialId: "cred_abc123" });
+
+// Extract data with AI
+const data = await page.extract({
+ prompt: "Extract all invoice numbers and amounts from the billing page",
+ schema: {
+ type: "array",
+ items: {
+ type: "object",
+ properties: {
+ invoice_number: { type: "string" },
+ amount: { type: "string" },
+ },
+ },
+ },
+});
+console.log(data);
+
+// Clean up
+await browser.close();
+await skyvern.close();
+```
diff --git a/docs/ts-sdk-reference/browser-profiles.mdx b/docs/ts-sdk-reference/browser-profiles.mdx
new file mode 100644
index 00000000..1655c580
--- /dev/null
+++ b/docs/ts-sdk-reference/browser-profiles.mdx
@@ -0,0 +1,133 @@
+---
+title: Browser Profiles
+subtitle: Save and reuse browser state across runs
+slug: ts-sdk-reference/browser-profiles
+---
+
+A browser profile is a snapshot of browser state — cookies, local storage, session data. Create a profile from a completed run, then load it into future workflow runs to skip login and setup steps.
+
+For conceptual background, see [Browser Profiles](/optimization/browser-profiles).
+
+---
+
+## `createBrowserProfile`
+
+Create a profile from a completed workflow run.
+
+```typescript
+const profile = await skyvern.createBrowserProfile({
+ name: "production-login",
+ workflow_run_id: "wr_abc123",
+});
+console.log(profile.browser_profile_id); // bpf_abc123
+```
+
+### Parameters
+
+| Parameter | Type | Required | Description |
+|-----------|------|----------|-------------|
+| `name` | `string` | Yes | Display name for the profile. |
+| `description` | `string` | No | Optional description. |
+| `workflow_run_id` | `string` | No | The workflow run ID to snapshot. The run must have used `persist_browser_session: true`. |
+| `browser_session_id` | `string` | No | The browser session ID to snapshot. |
+
+### Returns `BrowserProfile`
+
+| Field | Type | Description |
+|-------|------|-------------|
+| `browser_profile_id` | `string` | Unique ID. Starts with `bpf_`. |
+| `name` | `string` | Profile name. |
+| `description` | `string \| undefined` | Profile description. |
+| `created_at` | `string` | When the profile was created. |
+
+### Example: Create a profile from a login workflow
+
+```typescript
+// Step 1: Run a workflow with persist_browser_session
+const run = await skyvern.runWorkflow({
+ body: {
+ workflow_id: "wpid_login_flow",
+ parameters: { username: "demo@example.com" },
+ },
+ waitForCompletion: true,
+});
+
+// Step 2: Create a profile from the run
+const profile = await skyvern.createBrowserProfile({
+ name: "demo-account-login",
+ workflow_run_id: run.run_id,
+});
+
+// Step 3: Use the profile in future runs (skip login)
+const result = await skyvern.runWorkflow({
+ body: {
+ workflow_id: "wpid_extract_invoices",
+ browser_profile_id: profile.browser_profile_id,
+ },
+ waitForCompletion: true,
+});
+```
+
+
+Session archiving is asynchronous. If `createBrowserProfile` fails immediately after a workflow completes, wait a few seconds and retry.
+
+
+---
+
+## `listBrowserProfiles`
+
+List all browser profiles.
+
+```typescript
+const profiles = await skyvern.listBrowserProfiles({});
+for (const p of profiles) {
+ console.log(`${p.name} (${p.browser_profile_id})`);
+}
+```
+
+### Parameters
+
+| Parameter | Type | Required | Default | Description |
+|-----------|------|----------|---------|-------------|
+| `include_deleted` | `boolean` | No | `undefined` | Include soft-deleted profiles in the results. |
+
+### Returns `BrowserProfile[]`
+
+---
+
+## `getBrowserProfile`
+
+Get a single profile by ID.
+
+```typescript
+const profile = await skyvern.getBrowserProfile("bpf_abc123");
+console.log(profile.name);
+```
+
+### Parameters
+
+| Parameter | Type | Required | Description |
+|-----------|------|----------|-------------|
+| `profileId` | `string` | Yes | The browser profile ID. |
+
+### Returns `BrowserProfile`
+
+---
+
+## `deleteBrowserProfile`
+
+Delete a browser profile.
+
+```typescript
+await skyvern.deleteBrowserProfile("bpf_abc123");
+```
+
+### Parameters
+
+| Parameter | Type | Required | Description |
+|-----------|------|----------|-------------|
+| `profileId` | `string` | Yes | The browser profile ID to delete. |
+
+
+`browser_profile_id` only works with `runWorkflow`, not `runTask`. If you pass it to `runTask`, it will be silently ignored.
+
diff --git a/docs/ts-sdk-reference/browser-sessions.mdx b/docs/ts-sdk-reference/browser-sessions.mdx
new file mode 100644
index 00000000..fcae95ef
--- /dev/null
+++ b/docs/ts-sdk-reference/browser-sessions.mdx
@@ -0,0 +1,124 @@
+---
+title: Browser Sessions
+subtitle: Maintain live browser state between API calls
+slug: ts-sdk-reference/browser-sessions
+---
+
+A browser session is a persistent browser instance that stays alive between API calls. Use sessions to chain multiple tasks in the same browser without losing cookies, local storage, or login state.
+
+For conceptual background, see [Browser Sessions](/optimization/browser-sessions).
+
+---
+
+## `createBrowserSession`
+
+Spin up a new cloud browser session.
+
+```typescript
+const session = await skyvern.createBrowserSession({ timeout: 60 });
+console.log(session.browser_session_id); // pbs_abc123
+```
+
+### Parameters
+
+| Parameter | Type | Required | Default | Description |
+|-----------|------|----------|---------|-------------|
+| `timeout` | `number` | No | `60` | Session timeout in minutes (5–1440). Timer starts after the session is ready. |
+| `proxy_location` | `ProxyLocation` | No | `undefined` | Route browser traffic through a geographic proxy. |
+| `extensions` | `Extensions[]` | No | `undefined` | Browser extensions to install. Options: `"ad-blocker"`, `"captcha-solver"`. |
+| `browser_type` | `PersistentBrowserType` | No | `undefined` | Browser type. Options: `"chrome"`, `"msedge"`. |
+
+### Returns `BrowserSessionResponse`
+
+| Field | Type | Description |
+|-------|------|-------------|
+| `browser_session_id` | `string` | Unique ID. Starts with `pbs_`. |
+| `status` | `string \| undefined` | Current session status. |
+| `browser_address` | `string \| undefined` | CDP address for connecting to the browser. |
+| `app_url` | `string \| undefined` | Link to the live browser view in the Cloud UI. |
+| `timeout` | `number \| undefined` | Configured timeout in minutes. |
+| `started_at` | `string \| undefined` | When the session became ready. |
+| `created_at` | `string` | When the session was requested. |
+
+### Example: Chain multiple tasks in one session
+
+```typescript
+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);
+```
+
+---
+
+## `getBrowserSession`
+
+Get the status and details of a session.
+
+```typescript
+const session = await skyvern.getBrowserSession("pbs_abc123");
+console.log(session.status, session.browser_address);
+```
+
+### Parameters
+
+| Parameter | Type | Required | Description |
+|-----------|------|----------|-------------|
+| `browserSessionId` | `string` | Yes | The session ID. |
+
+### Returns `BrowserSessionResponse`
+
+---
+
+## `getBrowserSessions`
+
+List all active browser sessions.
+
+```typescript
+const sessions = await skyvern.getBrowserSessions();
+for (const s of sessions) {
+ console.log(`${s.browser_session_id} — ${s.status}`);
+}
+```
+
+### Returns `BrowserSessionResponse[]`
+
+---
+
+## `closeBrowserSession`
+
+Close a browser session and release its resources.
+
+```typescript
+await skyvern.closeBrowserSession("pbs_abc123");
+```
+
+### Parameters
+
+| Parameter | Type | Required | Description |
+|-----------|------|----------|-------------|
+| `browserSessionId` | `string` | Yes | The session ID to close. |
+
+
+Closing a session is irreversible. Any unsaved state (cookies, local storage) is lost unless you created a [browser profile](/ts-sdk-reference/browser-profiles) from it.
+
diff --git a/docs/ts-sdk-reference/complete-reference.mdx b/docs/ts-sdk-reference/complete-reference.mdx
new file mode 100644
index 00000000..47f16128
--- /dev/null
+++ b/docs/ts-sdk-reference/complete-reference.mdx
@@ -0,0 +1,618 @@
+---
+title: Complete Reference (For LLMs)
+subtitle: Every method, parameter, and type in one page
+slug: ts-sdk-reference/complete-reference
+---
+
+## Install and initialize
+
+```bash
+npm install @skyvern/client
+```
+
+```typescript
+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:**
+
+```typescript
+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, // Additional headers
+})
+```
+
+**Environments:**
+
+```typescript
+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
+
+```typescript
+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`
+
+```typescript
+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, // 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,
+ totp_identifier?: string,
+ totp_url?: string,
+ title?: string,
+ model?: Record,
+ user_agent?: string,
+ extra_http_headers?: Record,
+ browser_address?: string,
+ },
+ waitForCompletion?: boolean, // Poll until finished. Default: false.
+ timeout?: number, // Max wait (seconds). Default: 1800.
+}): Promise
+```
+
+**`TaskRunResponse` fields:**
+
+| Field | Type | Description |
+|-------|------|-------------|
+| `run_id` | `string` | Unique ID (`tsk_...`). |
+| `status` | `string` | `created` \| `queued` \| `running` \| `completed` \| `failed` \| `terminated` \| `timed_out` \| `canceled` |
+| `output` | `Record \| null` | Extracted data. `null` until completed. |
+| `failure_reason` | `string \| undefined` | Error description if failed. |
+| `downloaded_files` | `FileInfo[] \| undefined` | Files downloaded during the run. |
+| `recording_url` | `string \| undefined` | Session recording video URL. |
+| `screenshot_urls` | `string[] \| undefined` | Final screenshots. |
+| `app_url` | `string \| undefined` | Link to run in Skyvern UI. |
+| `step_count` | `number \| undefined` | Number of AI steps taken. |
+| `created_at` | `string` | When the run was created. |
+| `finished_at` | `string \| undefined` | When the run finished. |
+
+### `getRun`
+
+```typescript
+const run = await skyvern.getRun(runId: string): Promise
+```
+
+Returns status and results for any run (task or workflow).
+
+### `cancelRun`
+
+```typescript
+await skyvern.cancelRun(runId: string): Promise
+```
+
+### `getRunTimeline`
+
+```typescript
+const timeline = await skyvern.getRunTimeline(runId: string): Promise
+```
+
+### `getRunArtifacts`
+
+```typescript
+const artifacts = await skyvern.getRunArtifacts(
+ runId: string,
+ request?: { artifact_type?: ArtifactType | ArtifactType[] },
+): Promise
+```
+
+### `getArtifact`
+
+```typescript
+const artifact = await skyvern.getArtifact(artifactId: string): Promise
+```
+
+### `retryRunWebhook`
+
+```typescript
+await skyvern.retryRunWebhook(runId: string): Promise
+```
+
+---
+
+## Workflows
+
+### `runWorkflow`
+
+```typescript
+const result = await skyvern.runWorkflow({
+ body: {
+ workflow_id: string, // Required. Permanent ID (wpid_...).
+ parameters?: Record, // 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,
+ browser_address?: string,
+ },
+ template?: boolean,
+ waitForCompletion?: boolean, // Default: false.
+ timeout?: number, // Default: 1800.
+}): Promise
+```
+
+**`WorkflowRunResponse` fields:** Same as `TaskRunResponse` plus `run_with`, `ai_fallback`, `script_run`.
+
+### `createWorkflow`
+
+```typescript
+const workflow = await skyvern.createWorkflow({
+ body: { json_definition?: object, yaml_definition?: string },
+ folder_id?: string,
+}): Promise
+```
+
+**`Workflow` fields:** `workflow_id`, `workflow_permanent_id`, `version`, `title`, `workflow_definition`, `status`, `created_at`
+
+### `getWorkflows`
+
+```typescript
+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
+```
+
+### `getWorkflow`
+
+```typescript
+const workflow = await skyvern.getWorkflow(
+ workflowPermanentId: string,
+ request?: { version?: number; template?: boolean },
+): Promise
+```
+
+### `getWorkflowVersions`
+
+```typescript
+const versions = await skyvern.getWorkflowVersions(
+ workflowPermanentId: string,
+ request?: { template?: boolean },
+): Promise
+```
+
+### `updateWorkflow`
+
+```typescript
+const updated = await skyvern.updateWorkflow(
+ workflowId: string, // The version ID (not permanent ID).
+ request?: { json_definition?: object; yaml_definition?: string },
+): Promise
+```
+
+### `deleteWorkflow`
+
+```typescript
+await skyvern.deleteWorkflow(workflowId: string): Promise
+```
+
+---
+
+## Browser sessions
+
+A persistent browser instance that stays alive between API calls. Use to chain tasks without losing cookies or login state.
+
+### `createBrowserSession`
+
+```typescript
+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` fields:** `browser_session_id`, `status`, `browser_address`, `app_url`, `timeout`, `started_at`, `created_at`
+
+### `getBrowserSession`
+
+```typescript
+const session = await skyvern.getBrowserSession(browserSessionId: string): Promise
+```
+
+### `getBrowserSessions`
+
+```typescript
+const sessions = await skyvern.getBrowserSessions(): Promise
+```
+
+### `closeBrowserSession`
+
+```typescript
+await skyvern.closeBrowserSession(browserSessionId: string): Promise
+```
+
+### Session chaining example
+
+```typescript
+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`
+
+```typescript
+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,
+}): Promise
+```
+
+**`BrowserProfile` fields:** `browser_profile_id`, `name`, `description`, `created_at`
+
+### `listBrowserProfiles`
+
+```typescript
+const profiles = await skyvern.listBrowserProfiles({
+ include_deleted?: boolean,
+}): Promise
+```
+
+### `getBrowserProfile`
+
+```typescript
+const profile = await skyvern.getBrowserProfile(profileId: string): Promise
+```
+
+### `deleteBrowserProfile`
+
+```typescript
+await skyvern.deleteBrowserProfile(profileId: string): Promise
+```
+
+---
+
+## Credentials
+
+Store login information securely. Reference by ID instead of passing secrets in code.
+
+### `createCredential`
+
+```typescript
+const credential = await skyvern.createCredential({
+ name: string,
+ credential_type: CredentialType, // e.g. "password"
+ credential: object, // Password: { username: "...", password: "..." }
+}): Promise
+```
+
+### `getCredentials`
+
+```typescript
+const creds = await skyvern.getCredentials({
+ page?: number,
+ page_size?: number,
+}): Promise
+```
+
+### `getCredential`
+
+```typescript
+const cred = await skyvern.getCredential(credentialId: string): Promise
+```
+
+### `deleteCredential`
+
+```typescript
+await skyvern.deleteCredential(credentialId: string): Promise
+```
+
+### `sendTotpCode`
+
+Send a TOTP code to Skyvern during a run that requires 2FA.
+
+```typescript
+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
+```
+
+---
+
+## Helper methods
+
+### `login`
+
+Automate logging into a website using stored credentials.
+
+```typescript
+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,
+ 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
+```
+
+### `downloadFiles`
+
+```typescript
+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,
+ waitForCompletion?: boolean, // Default: false.
+ timeout?: number, // Default: 1800.
+}): Promise
+```
+
+### `uploadFile`
+
+```typescript
+const upload = await skyvern.uploadFile({ file }): Promise
+// Returns: { s3_uri: string, presigned_url: string }
+```
+
+---
+
+## Browser automation (Playwright + AI)
+
+The TypeScript SDK extends Playwright with AI capabilities via cloud browsers.
+
+### Launch browsers
+
+```typescript
+// 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
+
+```typescript
+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
+
+```typescript
+// 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`
+
+```typescript
+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
+
+```typescript
+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`:
+
+```typescript
+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`:
+
+```typescript
+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):
+
+```typescript
+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`:
+
+```typescript
+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.
diff --git a/docs/ts-sdk-reference/credentials.mdx b/docs/ts-sdk-reference/credentials.mdx
new file mode 100644
index 00000000..52b02dcb
--- /dev/null
+++ b/docs/ts-sdk-reference/credentials.mdx
@@ -0,0 +1,120 @@
+---
+title: Credentials
+subtitle: Store and manage authentication credentials securely
+slug: ts-sdk-reference/credentials
+---
+
+Credentials let you store login information (username/password, TOTP secrets) securely in Skyvern's vault. Reference them by ID in tasks and workflows instead of passing secrets in your code.
+
+---
+
+## `createCredential`
+
+Store a new credential.
+
+```typescript
+const credential = await skyvern.createCredential({
+ name: "my-app-login",
+ credential_type: "password",
+ credential: {
+ username: "demo@example.com",
+ password: "s3cur3-p4ss",
+ },
+});
+console.log(credential.credential_id);
+```
+
+### Parameters
+
+| Parameter | Type | Required | Description |
+|-----------|------|----------|-------------|
+| `name` | `string` | Yes | Display name for the credential. |
+| `credential_type` | `CredentialType` | Yes | Type of credential. |
+| `credential` | `object` | Yes | The credential data. Shape depends on `credential_type`. |
+
+### Returns `CredentialResponse`
+
+---
+
+## `getCredentials`
+
+List all credentials. Credential values are never returned — only metadata.
+
+```typescript
+const creds = await skyvern.getCredentials({});
+for (const c of creds) {
+ console.log(`${c.name} (${c.credential_id})`);
+}
+```
+
+### Parameters
+
+| Parameter | Type | Required | Default | Description |
+|-----------|------|----------|---------|-------------|
+| `page` | `number` | No | `undefined` | Page number. |
+| `page_size` | `number` | No | `undefined` | Results per page. |
+
+### Returns `CredentialResponse[]`
+
+---
+
+## `getCredential`
+
+Get a single credential's metadata by ID.
+
+```typescript
+const cred = await skyvern.getCredential("cred_abc123");
+console.log(cred.name, cred.credential_type);
+```
+
+### Parameters
+
+| Parameter | Type | Required | Description |
+|-----------|------|----------|-------------|
+| `credentialId` | `string` | Yes | The credential ID. |
+
+### Returns `CredentialResponse`
+
+---
+
+## `deleteCredential`
+
+Delete a credential.
+
+```typescript
+await skyvern.deleteCredential("cred_abc123");
+```
+
+### Parameters
+
+| Parameter | Type | Required | Description |
+|-----------|------|----------|-------------|
+| `credentialId` | `string` | Yes | The credential ID to delete. |
+
+---
+
+## `sendTotpCode`
+
+Send a TOTP (time-based one-time password) code to Skyvern during a run that requires 2FA. Call this when your webhook or polling detects that Skyvern is waiting for a TOTP code.
+
+```typescript
+await skyvern.sendTotpCode({
+ totp_identifier: "demo@example.com",
+ content: "123456",
+});
+```
+
+### Parameters
+
+| Parameter | Type | Required | Description |
+|-----------|------|----------|-------------|
+| `totp_identifier` | `string` | Yes | The identifier matching the `totp_identifier` used in the task/workflow. |
+| `content` | `string` | Yes | The TOTP code value. |
+| `task_id` | `string` | No | Associate with a specific task run. |
+| `workflow_id` | `string` | No | Associate with a specific workflow. |
+| `workflow_run_id` | `string` | No | Associate with a specific workflow run. |
+| `source` | `string` | No | Source of the TOTP code. |
+| `expired_at` | `string` | No | When this code expires (ISO 8601 format). |
+| `type` | `OtpType` | No | OTP type. |
+
+### Returns `TotpCode`
diff --git a/docs/ts-sdk-reference/error-handling.mdx b/docs/ts-sdk-reference/error-handling.mdx
new file mode 100644
index 00000000..eb5d713f
--- /dev/null
+++ b/docs/ts-sdk-reference/error-handling.mdx
@@ -0,0 +1,228 @@
+---
+title: Error Handling
+subtitle: Handle API errors, timeouts, and configure retries
+slug: ts-sdk-reference/error-handling
+---
+
+The SDK raises typed exceptions for API errors. All errors extend `SkyvernError` and include the HTTP status code, response body, and raw response.
+
+---
+
+## Error types
+
+| Exception | Status Code | When it's raised |
+|-----------|-------------|------------------|
+| `BadRequestError` | 400 | Invalid request parameters. |
+| `ForbiddenError` | 403 | Invalid or missing API key. |
+| `NotFoundError` | 404 | Resource (run, workflow, session) not found. |
+| `ConflictError` | 409 | Resource conflict (e.g., duplicate creation). |
+| `UnprocessableEntityError` | 422 | Request validation failed. |
+| `SkyvernError` | Any | Base class for all API errors. Catch this as a fallback. |
+| `SkyvernTimeoutError` | — | HTTP request timed out. |
+
+Import errors from the package:
+
+```typescript
+import { SkyvernError, SkyvernTimeoutError, SkyvernApi } from "@skyvern/client";
+
+// Base errors are top-level exports:
+// SkyvernError — base class for all API errors
+// SkyvernTimeoutError — HTTP request timed out
+
+// HTTP status error subclasses are accessed via the SkyvernApi namespace:
+// SkyvernApi.BadRequestError — 400
+// SkyvernApi.ForbiddenError — 403
+// SkyvernApi.NotFoundError — 404
+// SkyvernApi.ConflictError — 409
+// SkyvernApi.UnprocessableEntityError — 422
+```
+
+---
+
+## Catching errors
+
+```typescript
+import { Skyvern, SkyvernError, SkyvernApi } from "@skyvern/client";
+
+const skyvern = new Skyvern({ apiKey: "YOUR_API_KEY" });
+
+try {
+ const run = await skyvern.getRun("tsk_nonexistent");
+} catch (e) {
+ if (e instanceof SkyvernApi.NotFoundError) {
+ console.log(`Run not found: ${e.body}`);
+ } else if (e instanceof SkyvernError) {
+ console.log(`API error ${e.statusCode}: ${e.body}`);
+ }
+}
+```
+
+### Error properties
+
+Every error has these attributes:
+
+| Property | Type | Description |
+|----------|------|-------------|
+| `statusCode` | `number \| undefined` | HTTP status code. |
+| `body` | `unknown` | Response body (usually an object with error details). |
+| `rawResponse` | `RawResponse \| undefined` | The raw HTTP response. |
+| `message` | `string` | Human-readable error message. |
+
+---
+
+## Timeouts
+
+Two different timeouts apply:
+
+### HTTP request timeout
+
+Controls how long the SDK waits for the HTTP response from the Skyvern API. Set it in the constructor or per-request:
+
+```typescript
+// Global timeout (applies to all requests)
+const skyvern = new Skyvern({
+ apiKey: "YOUR_API_KEY",
+ timeoutInSeconds: 30,
+});
+
+// Per-request timeout
+const result = await skyvern.getRun("tsk_abc123", {
+ timeoutInSeconds: 10,
+});
+```
+
+When an HTTP request times out, a `SkyvernTimeoutError` is thrown.
+
+### Completion timeout
+
+Controls how long `waitForCompletion` polls before giving up. This is separate from the HTTP timeout:
+
+```typescript
+try {
+ const result = await skyvern.runTask({
+ body: {
+ prompt: "Extract data",
+ url: "https://example.com",
+ },
+ waitForCompletion: true,
+ timeout: 300, // Give up after 5 minutes
+ });
+} catch (e) {
+ if (e instanceof Error && e.message.includes("Timeout")) {
+ console.log("Task didn't complete in time");
+ }
+}
+```
+
+The completion timeout throws a standard JavaScript `Error` with a timeout message.
+
+---
+
+## Retries
+
+Configure automatic retries for transient failures. Set it in the constructor or per-request:
+
+```typescript
+// Global retries (default: 2)
+const skyvern = new Skyvern({
+ apiKey: "YOUR_API_KEY",
+ maxRetries: 3,
+});
+
+// Per-request retries
+const result = await skyvern.runTask(
+ {
+ body: {
+ prompt: "Extract product data",
+ url: "https://example.com/products",
+ },
+ },
+ { maxRetries: 5 },
+);
+```
+
+Retries apply to the HTTP request level (network errors, 5xx responses). They do not retry the entire task if it fails at the AI level — use `getRun` to check the status and re-run if needed.
+
+---
+
+## Abort requests
+
+Cancel in-flight requests using `AbortSignal`:
+
+```typescript
+const controller = new AbortController();
+
+// Cancel after 10 seconds
+setTimeout(() => controller.abort(), 10000);
+
+try {
+ const result = await skyvern.runTask(
+ {
+ body: {
+ prompt: "Extract data",
+ url: "https://example.com",
+ },
+ },
+ { abortSignal: controller.signal },
+ );
+} catch (e) {
+ if (e instanceof Error && e.name === "AbortError") {
+ console.log("Request was aborted");
+ }
+}
+```
+
+---
+
+## Run failure vs API errors
+
+There are two distinct failure modes:
+
+**API error** — The HTTP request itself failed. The SDK throws an exception.
+
+```typescript
+import { SkyvernError } from "@skyvern/client";
+
+try {
+ const result = await skyvern.runTask({
+ body: { prompt: "..." },
+ });
+} catch (e) {
+ if (e instanceof SkyvernError) {
+ console.log(`API call failed: ${e.statusCode}`);
+ }
+}
+```
+
+**Run failure** — The API call succeeded, but the task/workflow failed during execution. No exception is thrown. Check the `status` field:
+
+```typescript
+const result = await skyvern.runTask({
+ body: {
+ prompt: "Fill out the form",
+ url: "https://example.com",
+ },
+ waitForCompletion: true,
+});
+
+if (result.status === "failed") {
+ console.log(`Task failed: ${result.failure_reason}`);
+} else if (result.status === "timed_out") {
+ console.log(`Task exceeded step limit after ${result.step_count} steps`);
+} else if (result.status === "completed") {
+ console.log(`Success: ${JSON.stringify(result.output)}`);
+}
+```
+
+### Run statuses
+
+| Status | Description |
+|--------|-------------|
+| `created` | Run initialized, not yet queued. |
+| `queued` | Waiting for an available browser. |
+| `running` | AI is executing. |
+| `completed` | Finished successfully. |
+| `failed` | Encountered an error during execution. |
+| `terminated` | Manually stopped. |
+| `timed_out` | Exceeded step limit (`max_steps`). |
+| `canceled` | Canceled before starting. |
diff --git a/docs/ts-sdk-reference/helpers.mdx b/docs/ts-sdk-reference/helpers.mdx
new file mode 100644
index 00000000..c6d3e962
--- /dev/null
+++ b/docs/ts-sdk-reference/helpers.mdx
@@ -0,0 +1,164 @@
+---
+title: Helper Methods
+subtitle: High-level methods for common automation patterns
+slug: ts-sdk-reference/helpers
+---
+
+These methods wrap common multi-step patterns into single API calls. Under the hood, they create and run specialized workflows.
+
+---
+
+## `login`
+
+Automate logging into a website using stored credentials. This creates a login workflow, executes it, and optionally waits for completion.
+
+```typescript
+const result = await skyvern.login({
+ credential_type: "skyvern",
+ credential_id: "cred_abc123",
+ url: "https://app.example.com/login",
+ waitForCompletion: true,
+});
+console.log(result.status);
+```
+
+### Parameters
+
+| Parameter | Type | Required | Default | Description |
+|-----------|------|----------|---------|-------------|
+| `credential_type` | `CredentialType` | Yes | — | How credentials are stored. Options: `"skyvern"`, `"bitwarden"`, `"1password"`, `"azure_vault"`. |
+| `url` | `string` | No | `undefined` | The login page URL. |
+| `credential_id` | `string` | No | `undefined` | The Skyvern credential ID (when using `"skyvern"` type). |
+| `prompt` | `string` | No | `undefined` | Additional instructions for the AI during login. |
+| `browser_session_id` | `string` | No | `undefined` | Run login inside an existing browser session. |
+| `browser_address` | `string` | No | `undefined` | Connect to a browser at this CDP address. |
+| `proxy_location` | `ProxyLocation` | No | `undefined` | Route browser traffic through a geographic proxy. |
+| `webhook_url` | `string` | No | `undefined` | URL to receive a POST when the login finishes. |
+| `totp_identifier` | `string` | No | `undefined` | Identifier for TOTP verification. |
+| `totp_url` | `string` | No | `undefined` | URL to receive TOTP codes. |
+| `extra_http_headers` | `Record` | No | `undefined` | Additional HTTP headers. |
+| `max_screenshot_scrolling_times` | `number` | No | `undefined` | Number of screenshot scrolls. |
+| `waitForCompletion` | `boolean` | No | `false` | Block until the login finishes. |
+| `timeout` | `number` | No | `1800` | Max wait time in seconds. |
+
+**Bitwarden-specific parameters:**
+
+| Parameter | Type | Description |
+|-----------|------|-------------|
+| `bitwarden_collection_id` | `string` | Bitwarden collection ID. |
+| `bitwarden_item_id` | `string` | Bitwarden item ID. |
+
+**1Password-specific parameters:**
+
+| Parameter | Type | Description |
+|-----------|------|-------------|
+| `onepassword_vault_id` | `string` | 1Password vault ID. |
+| `onepassword_item_id` | `string` | 1Password item ID. |
+
+**Azure Key Vault-specific parameters:**
+
+| Parameter | Type | Description |
+|-----------|------|-------------|
+| `azure_vault_name` | `string` | Azure Key Vault name. |
+| `azure_vault_username_key` | `string` | Secret name for the username. |
+| `azure_vault_password_key` | `string` | Secret name for the password. |
+| `azure_vault_totp_secret_key` | `string` | Secret name for the TOTP secret. |
+
+### Returns `WorkflowRunResponse`
+
+### Example: Login then extract data
+
+```typescript
+const session = await skyvern.createBrowserSession({});
+
+// Login
+await skyvern.login({
+ credential_type: "skyvern",
+ credential_id: "cred_abc123",
+ url: "https://app.example.com/login",
+ browser_session_id: session.browser_session_id,
+ waitForCompletion: true,
+});
+
+// Now extract data from the authenticated session
+const result = await skyvern.runTask({
+ body: {
+ prompt: "Go to the billing page and extract all invoices",
+ browser_session_id: session.browser_session_id,
+ },
+ waitForCompletion: true,
+});
+console.log(result.output);
+```
+
+---
+
+## `downloadFiles`
+
+Navigate to a page and download files.
+
+```typescript
+const result = await skyvern.downloadFiles({
+ navigation_goal: "Download the latest monthly report PDF",
+ url: "https://app.example.com/reports",
+ waitForCompletion: true,
+});
+
+for (const f of result.downloaded_files ?? []) {
+ console.log(f.name);
+}
+```
+
+### Parameters
+
+| Parameter | Type | Required | Default | Description |
+|-----------|------|----------|---------|-------------|
+| `navigation_goal` | `string` | Yes | — | Natural language description of what to download. |
+| `url` | `string` | No | `undefined` | Starting page URL. |
+| `browser_session_id` | `string` | No | `undefined` | Run inside an existing browser session. |
+| `browser_profile_id` | `string` | No | `undefined` | Load a browser profile. |
+| `proxy_location` | `ProxyLocation` | No | `undefined` | Route through a geographic proxy. |
+| `webhook_url` | `string` | No | `undefined` | URL to receive a POST when the download finishes. |
+| `download_suffix` | `string` | No | `undefined` | Expected file extension to wait for (e.g., `".pdf"`). |
+| `download_timeout` | `number` | No | `undefined` | Max time to wait for the download in seconds. |
+| `max_steps_per_run` | `number` | No | `undefined` | Cap AI steps. |
+| `extra_http_headers` | `Record` | No | `undefined` | Additional HTTP headers. |
+| `waitForCompletion` | `boolean` | No | `false` | Block until the download finishes. |
+| `timeout` | `number` | No | `1800` | Max wait time in seconds. |
+
+### Returns `WorkflowRunResponse`
+
+The `downloaded_files` field contains the list of files that were downloaded.
+
+
+Unlike the Python SDK where `download_files` does not support `wait_for_completion`, the TypeScript SDK supports `waitForCompletion` on all methods including `downloadFiles`.
+
+
+---
+
+## `uploadFile`
+
+Upload a file to Skyvern's storage. Returns a presigned URL and S3 URI you can reference in tasks and workflows.
+
+```typescript
+import fs from "fs";
+
+const upload = await skyvern.uploadFile({
+ file: fs.createReadStream("data.csv"),
+});
+console.log(upload.s3_uri); // s3://skyvern-uploads/...
+console.log(upload.presigned_url); // https://...signed download URL
+```
+
+### Parameters
+
+| Parameter | Type | Required | Description |
+|-----------|------|----------|-------------|
+| `file` | `File \| ReadStream \| Blob` | Yes | The file to upload. |
+
+### Returns `UploadFileResponse`
+
+| Field | Type | Description |
+|-------|------|-------------|
+| `s3_uri` | `string` | S3 URI for the uploaded file. |
+| `presigned_url` | `string` | Pre-signed download URL. |
diff --git a/docs/ts-sdk-reference/overview.mdx b/docs/ts-sdk-reference/overview.mdx
new file mode 100644
index 00000000..47d32495
--- /dev/null
+++ b/docs/ts-sdk-reference/overview.mdx
@@ -0,0 +1,173 @@
+---
+title: TypeScript SDK
+subtitle: Install, authenticate, and configure the Skyvern TypeScript client
+slug: ts-sdk-reference/overview
+---
+
+The `@skyvern/client` package wraps the Skyvern REST API in a typed TypeScript client with built-in browser automation via Playwright.
+
+```bash
+npm install @skyvern/client
+```
+
+
+Requires Node.js 18+. Also compatible with Bun, Deno, and Cloudflare Workers.
+
+
+---
+
+## Initialize the client
+
+The `Skyvern` class provides both API methods and browser automation. All API methods return promises:
+
+```typescript
+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 parameters
+
+| Parameter | Type | Default | Description |
+|-----------|------|---------|-------------|
+| `apiKey` | `string` | — | **Required.** Your Skyvern API key. Get one at [app.skyvern.com/settings](https://app.skyvern.com/settings/api-keys). |
+| `environment` | `SkyvernEnvironment \| string` | `Cloud` | Target environment. Options: `Cloud`, `Staging`, `Local`. |
+| `baseUrl` | `string` | `undefined` | Override the API base URL. Use this for self-hosted deployments. |
+| `timeoutInSeconds` | `number` | `60` | HTTP request timeout in seconds. |
+| `maxRetries` | `number` | `2` | Number of times to retry failed requests. |
+| `headers` | `Record` | `undefined` | Additional headers included with every request. |
+
+---
+
+## Environments
+
+The SDK ships with three built-in environment URLs:
+
+```typescript
+import { SkyvernEnvironment } from "@skyvern/client";
+```
+
+| Environment | URL | When to use |
+|-------------|-----|-------------|
+| `SkyvernEnvironment.Cloud` | `https://api.skyvern.com` | Skyvern Cloud (default) |
+| `SkyvernEnvironment.Staging` | `https://api-staging.skyvern.com` | Staging environment |
+| `SkyvernEnvironment.Local` | `http://localhost:8000` | Local server started with `skyvern run server` |
+
+For a self-hosted instance at a custom URL, pass `baseUrl` instead:
+
+```typescript
+const skyvern = new Skyvern({
+ apiKey: "YOUR_API_KEY",
+ baseUrl: "https://skyvern.your-company.com",
+});
+```
+
+---
+
+## `waitForCompletion`
+
+By default, `runTask` and `runWorkflow` return immediately after the run is queued — you get a `run_id` and need to poll `getRun()` yourself. Pass `waitForCompletion: true` to have the SDK poll automatically until the run reaches a terminal state (`completed`, `failed`, `terminated`, `timed_out`, or `canceled`):
+
+```typescript
+// Returns only after the task finishes (up to 30 min by default)
+const result = await skyvern.runTask({
+ body: {
+ prompt: "Fill out the contact form",
+ url: "https://example.com/contact",
+ },
+ waitForCompletion: true,
+ timeout: 600, // give up after 10 minutes
+});
+
+// Without waitForCompletion — returns immediately
+const task = await skyvern.runTask({
+ body: {
+ prompt: "Fill out the contact form",
+ url: "https://example.com/contact",
+ },
+});
+console.log(task.run_id); // poll with skyvern.getRun(task.run_id)
+```
+
+| Parameter | Type | Default | Description |
+|-----------|------|---------|-------------|
+| `waitForCompletion` | `boolean` | `false` | Poll until the run finishes. |
+| `timeout` | `number` | `1800` | Maximum wait time in seconds. Throws `Error` if exceeded. |
+
+Supported on `runTask`, `runWorkflow`, `login`, and `downloadFiles`.
+
+---
+
+## Request options
+
+Every method accepts an optional second parameter for per-request overrides of timeout, retries, headers, and abort signal:
+
+```typescript
+const result = await skyvern.runTask(
+ {
+ body: {
+ prompt: "Extract data",
+ url: "https://example.com",
+ },
+ },
+ {
+ timeoutInSeconds: 120,
+ maxRetries: 3,
+ headers: { "x-custom-header": "value" },
+ },
+);
+```
+
+| Parameter | Type | Description |
+|-----------|------|-------------|
+| `timeoutInSeconds` | `number` | HTTP timeout for this request. |
+| `maxRetries` | `number` | Retry count for this request. |
+| `abortSignal` | `AbortSignal` | Signal to abort the request. |
+| `apiKey` | `string` | Override the API key for this request. |
+| `headers` | `Record` | Additional headers for this request. |
+
+These override the client-level defaults for that single call only.
+
+---
+
+## Next steps
+
+
+
+ Run browser automations with `runTask`
+
+
+ Create and run multi-step automations
+
+
+ Control cloud browsers with Playwright + AI
+
+
+ Handle errors and configure retries
+
+
diff --git a/docs/ts-sdk-reference/tasks.mdx b/docs/ts-sdk-reference/tasks.mdx
new file mode 100644
index 00000000..8185d282
--- /dev/null
+++ b/docs/ts-sdk-reference/tasks.mdx
@@ -0,0 +1,288 @@
+---
+title: Tasks
+subtitle: Run single browser automations with natural language
+slug: ts-sdk-reference/tasks
+---
+
+A task is a single browser automation. You describe what you want in natural language — Skyvern opens a browser, navigates to the URL, and executes the instructions with AI.
+
+For when to use tasks vs workflows, see [Run a Task](/running-automations/run-a-task).
+
+---
+
+## `runTask`
+
+Start a browser automation. Skyvern opens a cloud browser, navigates to the URL, and executes your prompt with AI.
+
+```typescript
+const result = await skyvern.runTask({
+ body: {
+ prompt: "Get the title of the top post",
+ url: "https://news.ycombinator.com",
+ },
+ waitForCompletion: true,
+});
+console.log(result.output);
+```
+
+### Parameters
+
+The `runTask` method accepts a single request object with the following shape:
+
+| Parameter | Type | Required | Default | Description |
+|-----------|------|----------|---------|-------------|
+| `body.prompt` | `string` | Yes | — | Natural language instructions for what the AI should do. |
+| `body.url` | `string` | No | `undefined` | Starting page URL. If omitted, the AI navigates from a blank page. |
+| `body.engine` | `RunEngine` | No | `"skyvern_v2"` | AI engine. Options: `"skyvern_v2"`, `"skyvern_v1"`, `"openai_cua"`, `"anthropic_cua"`, `"ui_tars"`. |
+| `body.max_steps` | `number` | No | `undefined` | Cap the number of AI steps to limit cost. Run terminates with `timed_out` if hit. |
+| `body.data_extraction_schema` | `Record \| string` | No | `undefined` | JSON schema constraining the output shape. |
+| `body.proxy_location` | `ProxyLocation` | No | `undefined` | Route the browser through a geographic proxy. |
+| `body.browser_session_id` | `string` | No | `undefined` | Run inside an existing [browser session](/optimization/browser-sessions). |
+| `body.publish_workflow` | `boolean` | No | `false` | Save the generated code as a reusable workflow. Only works with `skyvern_v2`. |
+| `body.webhook_url` | `string` | No | `undefined` | URL to receive a POST when the run finishes. |
+| `body.error_code_mapping` | `Record` | No | `undefined` | Map custom error codes to failure reasons. |
+| `body.totp_identifier` | `string` | No | `undefined` | Identifier for TOTP verification. |
+| `body.totp_url` | `string` | No | `undefined` | URL to receive TOTP codes. |
+| `body.title` | `string` | No | `undefined` | Display name for this run in the dashboard. |
+| `body.model` | `Record` | No | `undefined` | Override the output model definition. |
+| `body.user_agent` | `string` | No | `undefined` | Custom User-Agent header for the browser. |
+| `body.extra_http_headers` | `Record` | No | `undefined` | Additional HTTP headers injected into every browser request. |
+| `body.browser_address` | `string` | No | `undefined` | Connect to a browser at this CDP address instead of spinning up a new one. |
+| `waitForCompletion` | `boolean` | No | `false` | Block until the run finishes. |
+| `timeout` | `number` | No | `1800` | Max wait time in seconds when `waitForCompletion` is `true`. |
+
+### Returns `TaskRunResponse`
+
+| Field | Type | Description |
+|-------|------|-------------|
+| `run_id` | `string` | Unique identifier. Starts with `tsk_` for task runs. |
+| `status` | `string` | `"created"`, `"queued"`, `"running"`, `"completed"`, `"failed"`, `"terminated"`, `"timed_out"`, or `"canceled"`. |
+| `output` | `Record \| null` | Extracted data from the run. Shape depends on your prompt or `data_extraction_schema`. |
+| `downloaded_files` | `FileInfo[] \| undefined` | Files downloaded during the run. |
+| `recording_url` | `string \| undefined` | URL to the session recording video. |
+| `screenshot_urls` | `string[] \| undefined` | Final screenshots (most recent first). |
+| `failure_reason` | `string \| undefined` | Error description if the run failed. |
+| `app_url` | `string \| undefined` | Link to view this run in the Cloud UI. |
+| `step_count` | `number \| undefined` | Number of AI steps taken. |
+| `script_run` | `ScriptRunResponse \| undefined` | Code execution result if the run used generated code. |
+| `created_at` | `string` | When the run was created. |
+| `finished_at` | `string \| undefined` | When the run finished. |
+
+### Examples
+
+**Extract structured data:**
+
+```typescript
+const result = await skyvern.runTask({
+ body: {
+ prompt: "Extract the name, price, and rating of the top 3 products",
+ url: "https://example.com/products",
+ data_extraction_schema: {
+ type: "array",
+ items: {
+ type: "object",
+ properties: {
+ name: { type: "string" },
+ price: { type: "string" },
+ rating: { type: "number" },
+ },
+ },
+ },
+ },
+ waitForCompletion: true,
+});
+console.log(result.output);
+// [{ name: "Widget A", price: "$29.99", rating: 4.5 }, ...]
+```
+
+**Run inside an existing browser session:**
+
+```typescript
+const session = await skyvern.createBrowserSession({});
+
+const result = await skyvern.runTask({
+ body: {
+ prompt: "Log in and download the latest invoice",
+ url: "https://app.example.com/login",
+ browser_session_id: session.browser_session_id,
+ },
+ waitForCompletion: true,
+});
+```
+
+**Limit cost with max_steps:**
+
+```typescript
+const result = await skyvern.runTask({
+ body: {
+ prompt: "Fill out the contact form",
+ url: "https://example.com/contact",
+ max_steps: 10,
+ },
+ waitForCompletion: true,
+});
+```
+
+**Publish as a reusable workflow:**
+
+```typescript
+const result = await skyvern.runTask({
+ body: {
+ prompt: "Fill out the contact form with the provided data",
+ url: "https://example.com/contact",
+ publish_workflow: true,
+ },
+ waitForCompletion: true,
+});
+// The generated workflow is saved and can be re-triggered via runWorkflow
+```
+
+---
+
+## `getRun`
+
+Get the current status and results of any run (task or workflow).
+
+```typescript
+const run = await skyvern.getRun("tsk_v2_486305187432193504");
+console.log(run.status, run.output);
+```
+
+### Parameters
+
+| Parameter | Type | Required | Description |
+|-----------|------|----------|-------------|
+| `runId` | `string` | Yes | The run ID returned by `runTask` or `runWorkflow`. |
+
+### Returns `GetRunResponse`
+
+A discriminated union based on `run_type`. All variants share the same core fields as `TaskRunResponse` above, plus a `run_type` field (`task_v1`, `task_v2`, `openai_cua`, `anthropic_cua`, `ui_tars`, `workflow_run`).
+
+Workflow run responses additionally include `run_with` and `ai_fallback` fields.
+
+---
+
+## `cancelRun`
+
+Cancel a running or queued run.
+
+```typescript
+await skyvern.cancelRun("tsk_v2_486305187432193504");
+```
+
+### Parameters
+
+| Parameter | Type | Required | Description |
+|-----------|------|----------|-------------|
+| `runId` | `string` | Yes | The run ID to cancel. |
+
+The run transitions to `canceled` status. If the run has already finished, this is a no-op.
+
+---
+
+## `getRunTimeline`
+
+Get the step-by-step timeline of a run. Each entry represents one AI action with screenshots and reasoning.
+
+```typescript
+const timeline = await skyvern.getRunTimeline("tsk_v2_486305187432193504");
+for (const step of timeline) {
+ console.log(`Step ${step.order}: ${step.type} — ${step.status}`);
+}
+```
+
+### Parameters
+
+| Parameter | Type | Required | Description |
+|-----------|------|----------|-------------|
+| `runId` | `string` | Yes | The run ID. |
+
+### Returns `WorkflowRunTimeline[]`
+
+Each timeline entry contains step details including type, status, order, and associated artifacts.
+
+---
+
+## `getRunArtifacts`
+
+Get all artifacts (screenshots, recordings, generated code, etc.) for a run.
+
+```typescript
+const artifacts = await skyvern.getRunArtifacts("tsk_v2_486305187432193504");
+for (const artifact of artifacts) {
+ console.log(`${artifact.artifact_type}: ${artifact.uri}`);
+}
+```
+
+### Parameters
+
+| Parameter | Type | Required | Description |
+|-----------|------|----------|-------------|
+| `runId` | `string` | Yes | The run ID. |
+| `artifactType` | `ArtifactType \| ArtifactType[]` | No | Filter by artifact type. |
+
+### Returns `Artifact[]`
+
+---
+
+## `getArtifact`
+
+Get a single artifact by ID.
+
+```typescript
+const artifact = await skyvern.getArtifact("art_486305187432193504");
+console.log(artifact.uri);
+```
+
+### Parameters
+
+| Parameter | Type | Required | Description |
+|-----------|------|----------|-------------|
+| `artifactId` | `string` | Yes | The artifact ID. |
+
+### Returns `Artifact`
+
+---
+
+## `retryRunWebhook`
+
+Re-send the webhook notification for a completed run. Useful if your webhook endpoint was down when the run finished.
+
+```typescript
+await skyvern.retryRunWebhook("tsk_v2_486305187432193504");
+```
+
+### Parameters
+
+| Parameter | Type | Required | Description |
+|-----------|------|----------|-------------|
+| `runId` | `string` | Yes | The run ID. |
+
+---
+
+## Polling pattern
+
+If you don't use `waitForCompletion`, poll `getRun` manually:
+
+```typescript
+const task = await skyvern.runTask({
+ body: {
+ prompt: "Extract product data",
+ url: "https://example.com/products",
+ },
+});
+
+const terminalStatuses = ["completed", "failed", "terminated", "timed_out", "canceled"];
+let run;
+while (true) {
+ run = await skyvern.getRun(task.run_id);
+ if (terminalStatuses.includes(run.status)) break;
+ await new Promise((resolve) => setTimeout(resolve, 5000));
+}
+
+console.log(run.output);
+```
+
+
+For production, prefer `waitForCompletion: true` or [webhooks](/going-to-production/webhooks) over manual polling.
+
diff --git a/docs/ts-sdk-reference/workflows.mdx b/docs/ts-sdk-reference/workflows.mdx
new file mode 100644
index 00000000..e486a051
--- /dev/null
+++ b/docs/ts-sdk-reference/workflows.mdx
@@ -0,0 +1,273 @@
+---
+title: Workflows
+subtitle: Create and run multi-step browser automations
+slug: ts-sdk-reference/workflows
+---
+
+A workflow chains multiple steps (blocks) into a single automation. Workflows support loops, conditionals, data passing between steps, and code-based re-execution.
+
+For conceptual background, see [Build a Workflow](/multi-step-automations/build-a-workflow).
+
+---
+
+## `runWorkflow`
+
+Execute a workflow by its permanent ID. Skyvern opens a cloud browser and runs each block in sequence.
+
+```typescript
+const result = await skyvern.runWorkflow({
+ body: {
+ workflow_id: "wpid_abc123",
+ },
+ waitForCompletion: true,
+});
+console.log(result.output);
+```
+
+### Parameters
+
+| Parameter | Type | Required | Default | Description |
+|-----------|------|----------|---------|-------------|
+| `body.workflow_id` | `string` | Yes | — | The workflow's permanent ID (`wpid_...`). |
+| `body.parameters` | `Record` | No | `undefined` | Input parameters defined in the workflow. Keys must match parameter names. |
+| `body.browser_session_id` | `string` | No | `undefined` | Run inside an existing [browser session](/optimization/browser-sessions). |
+| `body.browser_profile_id` | `string` | No | `undefined` | Load a [browser profile](/optimization/browser-profiles) (cookies, storage) into the session. |
+| `body.proxy_location` | `ProxyLocation` | No | `undefined` | Route the browser through a geographic proxy. |
+| `body.webhook_url` | `string` | No | `undefined` | URL to receive a POST when the run finishes. |
+| `body.title` | `string` | No | `undefined` | Display name for this run in the dashboard. |
+| `body.totp_identifier` | `string` | No | `undefined` | Identifier for TOTP verification. |
+| `body.totp_url` | `string` | No | `undefined` | URL to receive TOTP codes. |
+| `body.user_agent` | `string` | No | `undefined` | Custom User-Agent header for the browser. |
+| `body.extra_http_headers` | `Record` | No | `undefined` | Additional HTTP headers injected into every browser request. |
+| `body.browser_address` | `string` | No | `undefined` | Connect to a browser at this CDP address. |
+| `template` | `boolean` | No | `undefined` | Run a template workflow. |
+| `waitForCompletion` | `boolean` | No | `false` | Block until the workflow finishes. |
+| `timeout` | `number` | No | `1800` | Max wait time in seconds when `waitForCompletion` is `true`. |
+
+### Returns `WorkflowRunResponse`
+
+| Field | Type | Description |
+|-------|------|-------------|
+| `run_id` | `string` | Unique identifier. Starts with `wr_` for workflow runs. |
+| `status` | `string` | `"created"`, `"queued"`, `"running"`, `"completed"`, `"failed"`, `"terminated"`, `"timed_out"`, or `"canceled"`. |
+| `output` | `Record \| null` | Extracted data from the workflow's output block. |
+| `downloaded_files` | `FileInfo[] \| undefined` | Files downloaded during the run. |
+| `recording_url` | `string \| undefined` | URL to the session recording. |
+| `failure_reason` | `string \| undefined` | Error description if the run failed. |
+| `app_url` | `string \| undefined` | Link to view this run in the Cloud UI. |
+| `step_count` | `number \| undefined` | Total AI steps taken across all blocks. |
+| `run_with` | `string \| undefined` | Whether the run used `"code"` or `"agent"`. |
+| `ai_fallback` | `boolean \| undefined` | Whether AI fallback was configured. |
+| `script_run` | `ScriptRunResponse \| undefined` | Code execution result. Contains `ai_fallback_triggered` if code was used. |
+
+### Examples
+
+**Pass parameters to a workflow:**
+
+```typescript
+const result = await skyvern.runWorkflow({
+ body: {
+ workflow_id: "wpid_invoice_extraction",
+ parameters: {
+ company_name: "Acme Corp",
+ date_range: "2025-01-01 to 2025-12-31",
+ },
+ },
+ waitForCompletion: true,
+});
+```
+
+**Run with a browser profile (skip login):**
+
+```typescript
+const result = await skyvern.runWorkflow({
+ body: {
+ workflow_id: "wpid_daily_report",
+ browser_profile_id: "bpf_abc123",
+ },
+ waitForCompletion: true,
+});
+```
+
+---
+
+## `createWorkflow`
+
+Create a new workflow from a JSON or YAML definition.
+
+```typescript
+const workflow = await skyvern.createWorkflow({
+ body: {
+ json_definition: {
+ title: "Extract Products",
+ workflow_definition: {
+ parameters: [
+ {
+ key: "target_url",
+ parameter_type: "workflow",
+ workflow_parameter_type: "string",
+ description: "URL to scrape",
+ },
+ ],
+ blocks: [
+ {
+ block_type: "task",
+ label: "extract",
+ prompt: "Extract the top 3 products with name and price",
+ url: "{{ target_url }}",
+ },
+ ],
+ },
+ },
+ },
+});
+console.log(workflow.workflow_permanent_id);
+```
+
+### Parameters
+
+| Parameter | Type | Required | Description |
+|-----------|------|----------|-------------|
+| `body.json_definition` | `object` | No | Workflow definition as a JSON object. |
+| `body.yaml_definition` | `string` | No | Workflow definition as a YAML string. |
+| `folder_id` | `string` | No | Folder to organize the workflow in. |
+
+You must provide either `body.json_definition` or `body.yaml_definition`.
+
+### Returns `Workflow`
+
+| Field | Type | Description |
+|-------|------|-------------|
+| `workflow_id` | `string` | Unique ID for this version. |
+| `workflow_permanent_id` | `string` | Stable ID across all versions. Use this to run workflows. |
+| `version` | `number` | Version number. |
+| `title` | `string` | Workflow title. |
+| `workflow_definition` | `WorkflowDefinition` | The full definition including blocks and parameters. |
+| `status` | `string \| undefined` | Workflow status. |
+| `created_at` | `string` | When the workflow was created. |
+
+---
+
+## `getWorkflows`
+
+List all workflows. Supports filtering and pagination.
+
+```typescript
+const workflows = await skyvern.getWorkflows({});
+for (const wf of workflows) {
+ console.log(`${wf.title} (${wf.workflow_permanent_id})`);
+}
+```
+
+### Parameters
+
+| Parameter | Type | Required | Default | Description |
+|-----------|------|----------|---------|-------------|
+| `page` | `number` | No | `undefined` | Page number for pagination. |
+| `page_size` | `number` | No | `undefined` | Number of results per page. |
+| `only_saved_tasks` | `boolean` | No | `undefined` | Only return saved tasks. |
+| `only_workflows` | `boolean` | No | `undefined` | Only return workflows (not saved tasks). |
+| `only_templates` | `boolean` | No | `undefined` | Only return templates. |
+| `title` | `string` | No | `undefined` | Filter by exact title. |
+| `search_key` | `string` | No | `undefined` | Search by title. |
+| `folder_id` | `string` | No | `undefined` | Filter by folder. |
+| `status` | `WorkflowStatus \| WorkflowStatus[]` | No | `undefined` | Filter by status. |
+
+### Returns `Workflow[]`
+
+---
+
+## `getWorkflow`
+
+Get a specific workflow by its permanent ID.
+
+```typescript
+const workflow = await skyvern.getWorkflow("wpid_abc123");
+console.log(workflow.title, `v${workflow.version}`);
+```
+
+### Parameters
+
+| Parameter | Type | Required | Description |
+|-----------|------|----------|-------------|
+| `workflowPermanentId` | `string` | Yes | The workflow's permanent ID. |
+| `version` | `number` | No | Specific version to retrieve. Defaults to latest. |
+| `template` | `boolean` | No | Whether to fetch a template workflow. |
+
+### Returns `Workflow`
+
+---
+
+## `getWorkflowVersions`
+
+List all versions of a workflow.
+
+```typescript
+const versions = await skyvern.getWorkflowVersions("wpid_abc123");
+for (const v of versions) {
+ console.log(`v${v.version} — ${v.modified_at}`);
+}
+```
+
+### Parameters
+
+| Parameter | Type | Required | Description |
+|-----------|------|----------|-------------|
+| `workflowPermanentId` | `string` | Yes | The workflow's permanent ID. |
+| `template` | `boolean` | No | Whether to fetch template versions. |
+
+### Returns `Workflow[]`
+
+---
+
+## `updateWorkflow`
+
+Update an existing workflow's definition.
+
+```typescript
+const updated = await skyvern.updateWorkflow("wpid_abc123", {
+ json_definition: {
+ title: "Extract Products Updated",
+ workflow_definition: {
+ blocks: [
+ {
+ block_type: "task",
+ label: "extract_data",
+ prompt: "Extract the top 5 products",
+ url: "https://example.com/products",
+ },
+ ],
+ parameters: [],
+ },
+ },
+});
+console.log(`Updated to v${updated.version}`);
+```
+
+### Parameters
+
+| Parameter | Type | Required | Description |
+|-----------|------|----------|-------------|
+| `workflowId` | `string` | Yes | The workflow's permanent ID (`wpid_...`). |
+| `json_definition` | `object` | No | Updated workflow definition as JSON. |
+| `yaml_definition` | `string` | No | Updated workflow definition as YAML. |
+
+### Returns `Workflow`
+
+Creates a new version of the workflow.
+
+---
+
+## `deleteWorkflow`
+
+Delete a workflow.
+
+```typescript
+await skyvern.deleteWorkflow("wf_abc123");
+```
+
+### Parameters
+
+| Parameter | Type | Required | Description |
+|-----------|------|----------|-------------|
+| `workflowId` | `string` | Yes | The workflow version ID to delete. |