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

User input and MFA

ctx.input pauses execution and lets Link display a form. Submitting resumes the same run.

Request a code

Inside a flow's exec(ctx):

ts
const { code } = await ctx.input("verification", {
  purpose: "operation",
  schema: z.object({ code: z.string().regex(/^\d{6}$/) }),
  fields: { code: { label: "Verification code", type: "text" } },
  message: "Enter the code sent by the website to confirm cancellation.",
});

Import z from zod. Use code in the next durable step to submit the actual website form.

PurposeExampleApp behavior
connectionExpired login needs MFAAsk the account holder to restore access
operationCancellation confirmationAsk the user to confirm this operation

Authentication flows default to connection purpose. Other flows default to operation purpose.

Hosted Link waits for a verification code from the account holder.

Rejected input

Inside exec(ctx), call your website's verification helper from a durable step:

ts
for (let attempt = 0; attempt < 3; attempt++) {
  const { code } = await ctx.input("verification", {
    purpose: "operation",
    schema: z.object({ code: z.string().min(1) }),
    fields: { code: { label: "Verification code", type: "text" } },
    ...(attempt
      ? {
          feedback: {
            tone: "error" as const,
            message: "Incorrect code. Try again.",
          },
        }
      : {}),
  });
  const accepted = await ctx.step("verify", () => submitCode(ctx, code));
  if (accepted) return { verified: true };
}
throw new FlowError("VERIFICATION_REJECTED", "Verification attempts exhausted");

submitCode(ctx, code) is your website helper: submit the real form and return whether the website accepted it. Import FlowError from the SDK and use a matching output schema for your flow.

Each request is a new occurrence; a previous code is not reused. Supported field types are text, email and password; schemas describe flat string fields.

Foreground and background

RunWhat happens when it needs input
User-driven endpointOpen hosted Link for the waiting run
Scheduled syncSend run.requires_input; let the user return later
Sync already waitingNo overlapping scheduled occurrence is created

Next: Resume a waiting run from your app.

← AuthenticationBrowser interactions and HTTP →
On this pageRequest a codeRejected inputForeground and background