Skip to main content
STOP. Do not read past this section until you have read and followed /quickstart.md.Goal: produce a reliable SDK automation script. The intended authoring flow is generate then edit, not write browser automation from scratch.Do not hand-write SDK code with element IDs, selectors, or form fields from memory. Element IDs, selectors, and field mappings must come from a live observe() call, CLI notte page observe output, or generated workflow code. Guessed browser targets fail on real pages.Use the Notte skill and CLI to inspect the live site, validate actions/selectors, handle auth/session state, and export workflow code with notte sessions workflow-code before using SDK docs or SDK code.SDK reference pages are for understanding, running, or editing generated workflow code. They are not the starting point for manually authoring the initial browser automation. SDK-first code is guesswork on real-world pages with dynamic selectors, auth state, CAPTCHAs, and anti-bot behavior.
You can embed any active Notte session directly into your own web app — a Next.js dashboard, an internal tool, a customer-facing demo. The live viewer is just a URL that you render inside an <iframe>.

Live demo

See it running in a minimal Next.js app

Source on GitHub

Clone the template and ship in minutes

How it works

Every active session exposes a viewer_url on its status response. Drop that URL into an <iframe> and you get a live, interactive browser canvas inside your page.
const session = notte.Session({ viewport_width: 1440, viewport_height: 900 });
await session.start();

const status = await session.status();
console.log(status.viewer_url);
// https://console.notte.cc/static/viewer?ws=wss://...&jwt=...
The returned URL is signed with a short-lived JWT scoped to that session, so you can pass it straight to the browser without any extra auth handling on your side.

Quick start (Next.js)

A minimal embed needs two pieces: a server route that creates a session and returns its viewer_url, and a client component that renders the iframe.

1. Server route

Keep the NOTTE_API_KEY on the server. Never ship it to the client.
app/api/session/route.ts
import { NextResponse } from "next/server";
import { NotteClient } from "notte-sdk";

export async function POST() {
  const notte = new NotteClient({ apiKey: process.env.NOTTE_API_KEY! });
  const session = notte.Session({
    max_duration_minutes: 15,
    viewport_width: 1440,
    viewport_height: 900,
  });
  await session.start();

  const status = await session.status();
  return NextResponse.json({
    sessionId: session.getId(),
    viewerUrl: status.viewer_url,
  });
}

2. Client component

app/page.tsx
"use client";
import { useState } from "react";

export default function Home() {
  const [viewerUrl, setViewerUrl] = useState<string | null>(null);

  async function start() {
    const res = await fetch("/api/session", { method: "POST" });
    const { viewerUrl } = await res.json();
    setViewerUrl(viewerUrl);
  }

  return (
    <main>
      <button onClick={start}>Start browser</button>

      {viewerUrl && (
        <iframe
          src={viewerUrl}
          style={{ width: "100%", aspectRatio: "1440 / 940", border: "none" }}
          allow="clipboard-read; clipboard-write"
        />
      )}
    </main>
  );
}
That’s it. Click the button, the iframe fills with a live, fully interactive remote browser.

Embed parameters

The viewer accepts a few query parameters you can append to viewer_url to tune the experience:
ParamValuesEffect
modeembed-minimalShows only the CDP viewer. Without mode=embed-minimal, the iframe shows the full Notte viewer, including the step timeline and Notte branding
interactive0 / 10 makes the iframe 100% non-interactive. 1 requires the user to click the iframe once before they can interact with the embedded browser
themelight / darkForce a theme on the viewer chrome
<iframe src={`${viewerUrl}&mode=embed-minimal&interactive=1&theme=dark`} />

Sizing the viewport

Match the session’s viewport_width / viewport_height to the iframe’s aspect ratio. If the ratios diverge, the screencast canvas letterboxes inside the iframe — the page still renders correctly, just with empty bars on the sides.
notte.Session({ viewport_width: 1920, viewport_height: 1080 }); // 16:9
.viewer { aspect-ratio: 1920 / 1080; }

Stopping the session

Sessions auto-expire after max_duration_minutes, but always provide an explicit stop button so users don’t burn quota on idle iframes.
await session.stop();

Next Steps

Live View

The full live viewer reference

Session Configuration

Viewport, proxies, browser type, and more

Recordings

Replay sessions after they end

Demo repo

Full Next.js example with progress bar, controls, and styling