home / skills / openclaw / skills / functions

functions skill

/skills/peytoncasper/functions

This skill guides you to deploy serverless browser automation with the official bb CLI, from setup to invocation.

npx playbooks add skill openclaw/skills --skill functions

Review the files below or copy the command above to add this skill to your agents.

Files (2)
SKILL.md
6.7 KB
---
name: functions
description: Guide Claude through deploying serverless browser automation using the official bb CLI
---

# Browserbase Functions Skill

Guide Claude through deploying serverless browser automation using the official `bb` CLI.

## When to Use

Use this skill when:
- User wants to deploy automation to run on a schedule
- User needs a webhook endpoint for browser automation
- User wants to run automation in the cloud (not locally)
- User asks about Browserbase Functions

## Prerequisites

### 1. Get Credentials

Get API key and Project ID from: https://browserbase.com/settings

### 2. Set Environment Variables

Set directly:
```bash
export BROWSERBASE_API_KEY="your_api_key"
export BROWSERBASE_PROJECT_ID="your_project_id"
```

## Creating a Function Project

### 1. Initialize with Official CLI

```bash
pnpm dlx @browserbasehq/sdk-functions init my-function
cd my-function
```

This creates:
```
my-function/
├── package.json
├── index.ts        # Your function code
└── .env            # Add credentials here
```

### 2. Add Credentials to .env

```bash
# Copy from stored credentials
echo "BROWSERBASE_API_KEY=$BROWSERBASE_API_KEY" >> .env
echo "BROWSERBASE_PROJECT_ID=$BROWSERBASE_PROJECT_ID" >> .env
```

Or manually edit `.env`:
```
BROWSERBASE_API_KEY=your_api_key
BROWSERBASE_PROJECT_ID=your_project_id
```

### 3. Install Dependencies

```bash
pnpm install
```

## Function Structure

```typescript
import { defineFn } from "@browserbasehq/sdk-functions";
import { chromium } from "playwright-core";

defineFn("my-function", async (context) => {
  const { session, params } = context;
  
  // Connect to browser
  const browser = await chromium.connectOverCDP(session.connectUrl);
  const page = browser.contexts()[0]!.pages()[0]!;
  
  // Your automation
  await page.goto(params.url || "https://example.com");
  const title = await page.title();
  
  // Return JSON-serializable result
  return { success: true, title };
});
```

**Key objects:**
- `context.session.connectUrl` - CDP endpoint to connect Playwright
- `context.params` - Input parameters from invocation

## Development Workflow

### 1. Start Dev Server

```bash
pnpm bb dev index.ts
```

Server runs at `http://127.0.0.1:14113`

### 2. Test Locally

```bash
curl -X POST http://127.0.0.1:14113/v1/functions/my-function/invoke \
  -H "Content-Type: application/json" \
  -d '{"params": {"url": "https://news.ycombinator.com"}}'
```

### 3. Iterate

The dev server auto-reloads on file changes. Use `console.log()` for debugging - output appears in the terminal.

## Deploying

### Publish to Browserbase

```bash
pnpm bb publish index.ts
```

Output:
```
Function published successfully
Build ID: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
Function ID: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
```

**Save the Function ID** - you need it to invoke.

## Invoking Deployed Functions

### Via curl

```bash
# Start invocation
curl -X POST "https://api.browserbase.com/v1/functions/FUNCTION_ID/invoke" \
  -H "Content-Type: application/json" \
  -H "x-bb-api-key: $BROWSERBASE_API_KEY" \
  -d '{"params": {"url": "https://example.com"}}'

# Response: {"id": "INVOCATION_ID"}

# Poll for result
curl "https://api.browserbase.com/v1/functions/invocations/INVOCATION_ID" \
  -H "x-bb-api-key: $BROWSERBASE_API_KEY"
```

### Via Code

```typescript
async function invokeFunction(functionId: string, params: object) {
  // Start invocation
  const invokeRes = await fetch(
    `https://api.browserbase.com/v1/functions/${functionId}/invoke`,
    {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'x-bb-api-key': process.env.BROWSERBASE_API_KEY!,
      },
      body: JSON.stringify({ params }),
    }
  );
  const { id: invocationId } = await invokeRes.json();

  // Poll until complete
  while (true) {
    await new Promise(r => setTimeout(r, 5000));
    
    const statusRes = await fetch(
      `https://api.browserbase.com/v1/functions/invocations/${invocationId}`,
      { headers: { 'x-bb-api-key': process.env.BROWSERBASE_API_KEY! } }
    );
    const result = await statusRes.json();
    
    if (result.status === 'COMPLETED') return result.results;
    if (result.status === 'FAILED') throw new Error(result.error);
  }
}
```

## Common Patterns

### Parameterized Scraping

```typescript
defineFn("scrape", async ({ session, params }) => {
  const browser = await chromium.connectOverCDP(session.connectUrl);
  const page = browser.contexts()[0]!.pages()[0]!;
  
  await page.goto(params.url);
  await page.waitForSelector(params.selector);
  
  const items = await page.$$eval(params.selector, els => 
    els.map(el => el.textContent?.trim())
  );
  
  return { url: params.url, items };
});
```

### With Authentication

```typescript
defineFn("authenticated-action", async ({ session, params }) => {
  const browser = await chromium.connectOverCDP(session.connectUrl);
  const page = browser.contexts()[0]!.pages()[0]!;
  
  // Login
  await page.goto("https://example.com/login");
  await page.fill('[name="email"]', params.email);
  await page.fill('[name="password"]', params.password);
  await page.click('button[type="submit"]');
  await page.waitForURL('**/dashboard');
  
  // Do authenticated work
  const data = await page.textContent('.user-data');
  return { data };
});
```

### Error Handling

```typescript
defineFn("safe-scrape", async ({ session, params }) => {
  const browser = await chromium.connectOverCDP(session.connectUrl);
  const page = browser.contexts()[0]!.pages()[0]!;
  
  try {
    await page.goto(params.url, { timeout: 30000 });
    await page.waitForSelector(params.selector, { timeout: 10000 });
    
    const data = await page.textContent(params.selector);
    return { success: true, data };
  } catch (error) {
    return { 
      success: false, 
      error: error instanceof Error ? error.message : 'Unknown error' 
    };
  }
});
```

## CLI Reference

| Command | Description |
|---------|-------------|
| `pnpm dlx @browserbasehq/sdk-functions init <name>` | Create new project |
| `pnpm bb dev <file>` | Start local dev server |
| `pnpm bb publish <file>` | Deploy to Browserbase |

## Troubleshooting

### "Missing API key"
```bash
# Check .env file has credentials
cat .env

# Or set for current shell
export BROWSERBASE_API_KEY="your_key"
export BROWSERBASE_PROJECT_ID="your_project"
```

### Dev server won't start
```bash
# Make sure SDK is installed
pnpm add @browserbasehq/sdk-functions

# Or use npx
npx @browserbasehq/sdk-functions dev index.ts
```

### Function times out
- Max execution time is 15 minutes
- Add specific timeouts to page operations
- Use `waitForSelector` instead of sleep

### Can't connect to browser
- Check `session.connectUrl` is being used correctly
- Ensure you're using `chromium.connectOverCDP()` not `chromium.launch()`

Overview

This skill guides Claude through deploying serverless browser automation using the official bb CLI and Browserbase Functions SDK. It explains how to initialize a function project, connect Playwright to the remote browser, run local development, and publish to Browserbase. The goal is to get reliable, scheduled or on-demand browser automation running in the cloud quickly.

How this skill works

You scaffold a function project with the official CLI, add your Browserbase API key and project ID, and author a TypeScript function that connects to a remote Chromium instance via the session.connectUrl. During development you run a local dev server for iterative testing; when ready you publish the function with pnpm bb publish and invoke it via the API or curl. Invocations return an ID you poll until the function completes, delivering JSON-serializable results.

When to use it

  • You need cloud-hosted browser automation instead of running locally
  • You want scheduled jobs or webhook-triggered browser tasks
  • You need a simple HTTP API to invoke Playwright-based automations
  • You want to centralize credentials and run headless browsers at scale

Best practices

  • Store BROWSERBASE_API_KEY and BROWSERBASE_PROJECT_ID in .env or secure CI secrets
  • Use session.connectUrl and chromium.connectOverCDP() — do not launch local browsers in functions
  • Return only JSON-serializable data from functions to avoid serialization issues
  • Add targeted timeouts and waitForSelector calls instead of blind sleeps to reduce flakiness
  • Log with console.log during development; monitor function logs after deploy

Example use cases

  • Parameterized scraping: fetch items from a URL and selector passed as params
  • Authenticated actions: log in, navigate to a dashboard, and extract user data
  • Scheduled reports: run nightly captures of a page and store results in a database
  • Webhook processing: receive an event, visit a page, and return structured output for downstream systems

FAQ

How do I test a function locally?

Run pnpm bb dev index.ts to start the local dev server, then POST to /v1/functions/<name>/invoke on 127.0.0.1:14113 with JSON {"params": {...}}.

How do I invoke a deployed function?

POST to https://api.browserbase.com/v1/functions/FUNCTION_ID/invoke with x-bb-api-key header and params JSON, then poll the invocations endpoint with the returned invocation ID until status is COMPLETED.

What causes connection failures to the browser?

Ensure you use context.session.connectUrl with chromium.connectOverCDP() and that your function uses the Browserbase-provided session. Do not call chromium.launch() inside the cloud function.