> ## Documentation Index
> Fetch the complete documentation index at: https://docs.browserbase.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Write a Function

> Define Browserbase Functions, validate parameters, configure browser sessions, and publish multiple handlers.

Use `defineFn` from `@browserbasehq/sdk-functions` to register a named handler:

```typescript Node.js theme={null}
import { defineFn } from "@browserbasehq/sdk-functions";
import { chromium } from "playwright-core";
import { z } from "zod";

defineFn(
  "extract-title",
  async (context, params) => {
    const browser = await chromium.connectOverCDP(
      context.session.connectUrl,
    );
    const page = browser.contexts()[0]!.pages()[0]!;

    await page.goto(params.url);

    return {
      sessionId: context.session.id,
      title: await page.title(),
    };
  },
  {
    parametersSchema: z.object({
      url: z.string().url(),
    }),
    sessionConfig: {
      browserSettings: {
        solveCaptchas: true,
      },
    },
  },
);
```

## `defineFn` arguments

`defineFn` accepts a name, a handler, and an optional configuration object.

### Name

The name identifies a Function within a Browserbase project. Each Function in an entrypoint must have a unique name.

Publishing another definition with the same name creates a new version of that Function. Callers continue to use the same Function ID.

### Handler

The async handler receives `context` and `params`.

`context.session` contains:

```typescript Node.js theme={null}
{
  connectUrl: string;
  id: string;
}
```

Use `connectUrl` to attach Stagehand, Playwright, or Puppeteer to the browser that Browserbase created for the invocation. Use `id` to inspect the session or add it to your logs.

`params` contains the object supplied in the invoke request. The request can contain up to 64 KB of serialized JSON.

The handler can return a JSON-serializable string, number, boolean, array, or object. Browserbase stores non-empty return values in the invocation's `results` field. A handler that returns `null` or `undefined` completes without `results`.

### Configuration

The optional third argument supports:

* `parametersSchema` to validate invocation parameters with Zod.
* `sessionConfig` to set the default browser session configuration for every invocation.

## Validate parameters

Define `parametersSchema` with Zod:

```typescript Node.js theme={null}
import { defineFn } from "@browserbasehq/sdk-functions";
import { z } from "zod";

defineFn(
  "search",
  async (_context, params) => {
    return { query: params.query, limit: params.limit };
  },
  {
    parametersSchema: z.object({
      query: z.string().min(1),
      limit: z.number().int().min(1).max(20),
    }),
  },
);
```

Use a schema for every Function that accepts external input. It documents the expected shape and rejects invalid values before your browser logic uses them.

## Configure the browser session

Set `sessionConfig` when every invocation needs the same browser settings:

```typescript Node.js theme={null}
defineFn("authenticated-task", handler, {
  sessionConfig: {
    browserSettings: {
      context: {
        id: "YOUR_CONTEXT_ID",
        persist: true,
      },
    },
    proxies: true,
    timeout: 600,
  },
});
```

Functions support most [Create a Session](/reference/api/create-a-session) options. They don't support `region` or `keepAlive`. The invocation timeout must be between 60 and 900 seconds.

Callers can override supported defaults for one invocation with [`sessionCreateParams`](/platform/functions/invoke#override-session-settings).

## Connect a browser library

<Tabs>
  <Tab title="Stagehand">
    Connect Stagehand to the Function's browser:

    ```typescript Node.js theme={null}
    import { defineFn } from "@browserbasehq/sdk-functions";
    import { localBrowser, Stagehand } from "@browserbasehq/stagehand";

    defineFn("stagehand-task", async (context) => {
      const browser = await localBrowser.connect({
        cdpUrl: context.session.connectUrl,
      });
      const stagehand = await Stagehand.create({ browser });
      const page = await browser.context.activePage();

      await page.goto("https://example.com");
      await stagehand.act("Click the More information link");

      return { url: page.url() };
    });
    ```
  </Tab>

  <Tab title="Playwright">
    Connect Playwright over CDP:

    ```typescript Node.js theme={null}
    import { defineFn } from "@browserbasehq/sdk-functions";
    import { chromium } from "playwright-core";

    defineFn("playwright-task", async (context) => {
      const browser = await chromium.connectOverCDP(
        context.session.connectUrl,
      );
      const page = browser.contexts()[0]!.pages()[0]!;

      await page.goto("https://example.com");

      return { title: await page.title() };
    });
    ```
  </Tab>

  <Tab title="Puppeteer">
    Connect Puppeteer with the browser WebSocket endpoint:

    ```typescript Node.js theme={null}
    import { defineFn } from "@browserbasehq/sdk-functions";
    import puppeteer from "puppeteer-core";

    defineFn("puppeteer-task", async (context) => {
      const browser = await puppeteer.connect({
        browserWSEndpoint: context.session.connectUrl,
      });
      const page = (await browser.pages())[0]!;

      await page.goto("https://example.com");

      return { title: await page.title() };
    });
    ```
  </Tab>
</Tabs>

<Note>
  A Function can also run a custom browser agent loop. Connect its browser tool to `context.session.connectUrl`. If you don't need to own the model loop, use [Browserbase Agents](/platform/agents/overview).
</Note>

## Publish multiple Functions

The publish command follows the entrypoint's import graph. Each `defineFn` call in that graph becomes a Function.

For a single file, define each Function in the entrypoint:

```typescript Node.js theme={null}
import { defineFn } from "@browserbasehq/sdk-functions";

defineFn("first-task", async () => ({ task: 1 }));
defineFn("second-task", async () => ({ task: 2 }));
```

For multiple files, import every file that registers a Function:

```typescript Node.js theme={null}
// index.ts
import "./functions/extract-title.js";
import "./functions/take-screenshot.js";
```

Then [deploy from the CLI](/platform/functions/deploy#deploy-from-the-cli).

## Handle errors and logs

An unhandled error marks the invocation as failed. Catch an error only when your Function can add useful context or return a valid partial result.

Use `console.log`, `console.warn`, and `console.error` for runtime logs. Browserbase captures this output in the [invocation logs](/reference/api/get-invocation-logs).

Browserbase closes the browser session when the handler completes. You don't need to close it in your code.

<CardGroup cols={2}>
  <Card title="Deploy Functions" icon="cloud-arrow-up" href="/platform/functions/deploy">
    Publish from the CLI or the Playground.
  </Card>

  <Card title="Invoke a Function" icon="terminal" href="/platform/functions/invoke">
    Pass parameters and retrieve asynchronous results.
  </Card>

  <Card title="Functions limits" icon="gauge" href="/platform/functions/limits">
    Check bundle, timeout, storage, and region constraints.
  </Card>
</CardGroup>
