browserlayer / docsPrivate alphaDashboard ↗

Start here

Websites → endpoints and syncsInstallRun your first endpointAtlas walkthrough

Build integrations

Project structureCapture a websiteBuild with your coding agentEndpointsSteps and connector flowsTyped referencesAuthenticationUser input and MFABrowser interactions and HTTPSync recordsCheckpoints and deletionsTest an integrationDebug in Studio

Use in your app

Connect accounts with hosted LinkCall an endpointConsume sync recordsReceive webhooks

Deploy

Deploy and verify

Reference

SDK referenceHTTP APICLI reference
Build integrationsMarkdown

Authentication

Browserlayer calls two reserved connector flows. Your app opens Link instead of invoking them as endpoints.

FileRoleOutput schema
connector/flows/authenticate.tsEstablish a connection; collect credentials and MFAconnectionResult
connector/flows/reauthenticate.tsReuse the session or restore itconnectionCheckResult

Authenticate

connector/flows/authenticate.ts — minimal website-specific example:

ts
import { createFlow, connectionResult, form } from "@browserlayer/sdk";
import { z } from "zod";

const credentials = form(
  z.object({ email: z.email(), password: z.string().min(1) }),
  {
    email: { label: "Email", type: "email" },
    password: { label: "Password", type: "password" },
  },
);

export default createFlow({
  description: "Sign in to the website.",
  input: z.object({}),
  output: connectionResult,
  initialInput: { id: "credentials", form: credentials },
  async exec(ctx) {
    await ctx.step("open", async () => {
      await (await ctx.browser()).goto("https://example.com/login");
    });
    const values = await ctx.input("credentials", credentials);
    await ctx.step("sign-in", async () => {
      const page = await ctx.browser();
      await page.getByLabel("Email").fill(values.email);
      await page.getByLabel("Password").fill(values.password);
      await page.getByRole("button", { name: "Sign in" }).click();
      await page.getByRole("heading", { name: "Your account" }).waitFor();
      await ctx.connection!.data.set("credentials", values);
    });
    return { authenticated: true as const };
  },
});

Replace the URL and selectors using your capture. For an executable login with MFA, use Atlas.

Reauthenticate

Check the retained session first. If expired, call authentication or restore it with saved connection data. Reauthentication may request input and resume within the current run.

ts
import { createFlow, connectionCheckResult } from "@browserlayer/sdk";
import { z } from "zod";
import authenticate from "./authenticate.js";

export default createFlow({
  description: "Restore the account session when needed.",
  connection: "required",
  input: z.object({}),
  output: connectionCheckResult,
  async exec(ctx) {
    const connected = await ctx.step("check-session", async () => {
      const page = await ctx.browser();
      return page.getByRole("heading", { name: "Your account" }).isVisible();
    });
    if (!connected) await ctx.run(authenticate, {});
    return { status: "connected" as const };
  },
});

Returning { status: "requires_input", reason: "verification_required" } instead of requesting input fails the business run with CONNECTION_REQUIRES_INPUT. The app must reconnect and invoke again. Use inline input when you want the original run to continue.

Test a connection

sh
browserlayer connections create atlas-demo --local --wait
browserlayer connections list --local
browserlayer connections check <connection-id> --local --wait
browserlayer connections reconnect <connection-id> --local --wait
← Typed referencesUser input and MFA →
On this pageAuthenticateReauthenticateTest a connection