Skip to main content
If you don’t want to run your own browser harness/session, use our managed browser runner instead:

What is Glance?

Glance is Morph’s UI testing model: It takes in context on your PR and any natural language instructions on something specific that needs to be tested. This is the right API when you want to own the browser session and harness (Playwright, Selenium, Browserbase, Steel, etc.) but still use Morph’s model for step-by-step decisions.

When to use it

  • Bring-your-own harness: you already have a browser runner and want the model as a “brain”.
  • Full control: you need custom tools, custom execution, or custom infra.
  • Stateful sessions: you want Morph to keep a server-side plan + step history and you just send observations.
If you just want “run a browser test from a prompt”, use Browser Automation.

API (what goes in, what comes out)

Glance is exposed as a stateful harness session API:
  • Create session: POST /harness/sessions
  • Next step: POST /harness/sessions/{session_id}/step

What you send

  • Session creation: url, diff, instructions, and optional tools (Gemini functionDeclarations).
  • Each step:
    • screenshot: base64 PNG/JPEG
    • context: optional metadata like current_url, page_title, viewport
    • client_step_id: optional idempotency key (recommended)

What you get back

  • step_index: 0-based index assigned by the server (useful for logging + ordering)
  • tool_calls: the tool calls to execute in your harness
  • is_done: whether the session is complete
  • done_reason: optional completion reason
Loop: execute tool_calls → take a new screenshot → call /step again.

Tool definitions (Gemini functionDeclarations)

You provide tools as a list of “function declarations”: name, description, and a JSON-schema-like parameters object. Here’s a minimal set that works well for browser harnesses:
[
  {
    "name": "navigate",
    "description": "Navigate the browser to a URL",
    "parameters": {
      "type": "object",
      "properties": {
        "url": { "type": "string", "description": "The URL to navigate to" }
      },
      "required": ["url"]
    }
  },
  {
    "name": "click",
    "description": "Click at screen coordinates",
    "parameters": {
      "type": "object",
      "properties": {
        "x": { "type": "number", "description": "X coordinate (pixels)" },
        "y": { "type": "number", "description": "Y coordinate (pixels)" }
      },
      "required": ["x", "y"]
    }
  },
  {
    "name": "type_text",
    "description": "Type text into the currently focused field",
    "parameters": {
      "type": "object",
      "properties": {
        "text": { "type": "string", "description": "Text to type" }
      },
      "required": ["text"]
    }
  },
  {
    "name": "scroll",
    "description": "Scroll the page",
    "parameters": {
      "type": "object",
      "properties": {
        "direction": { "type": "string", "enum": ["up", "down"] },
        "amount": { "type": "number", "description": "Pixels to scroll" }
      },
      "required": ["direction"]
    }
  },
  {
    "name": "done",
    "description": "Stop the session when the goal is met",
    "parameters": {
      "type": "object",
      "properties": {
        "reason": { "type": "string", "description": "Why we are done" }
      },
      "required": ["reason"]
    }
  }
]

Minimal example (JavaScript)

This shows the full loop at a high level (your harness executes the tools and captures screenshots):
import { readFileSync } from "node:fs";

const API_KEY = process.env.MORPH_API_KEY;
const API_URL = "https://browser.morphllm.com";

// 1) Create session
const createRes = await fetch(`${API_URL}/harness/sessions`, {
  method: "POST",
  headers: {
    Authorization: `Bearer ${API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    url: "https://github.com",
    instructions: "Get to the login page",
    tools: [/* functionDeclarations */],
  }),
});

const { session_id } = await createRes.json();

// 2) Step (send observation)
const screenshotB64 = readFileSync("screenshot.b64", "utf8").trim();
const stepRes = await fetch(`${API_URL}/harness/sessions/${session_id}/step`, {
  method: "POST",
  headers: {
    Authorization: `Bearer ${API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    screenshot: screenshotB64,
    client_step_id: "step-0001",
    context: { current_url: "https://github.com", page_title: "GitHub" },
  }),
});

const step = await stepRes.json();
console.log("step_index:", step.step_index);
console.log("tool_calls:", step.tool_calls);

Quick start (cURL)

curl -X POST "https://browser.morphllm.com/harness/sessions" \
  -H "Authorization: Bearer $MORPH_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://github.com",
    "instructions": "Get to the login page",
    "tools": [ /* functionDeclarations */ ]
  }'

Response format

Each step returns:
  • step_index: 0-based index assigned by the server
  • tool_calls: list of tool calls to execute in your harness
  • is_done: whether the session should stop
  • done_reason: optional stop explanation
Example:
{
  "step_index": 3,
  "tool_calls": [
    {
      "name": "click",
      "args": { "x": 942, "y": 20 }
    }
  ],
  "is_done": false,
  "done_reason": null
}

Notes

  • Idempotency: send client_step_id so retries don’t create duplicate steps.
  • Statefulness: sessions store a hidden plan and step history server-side; you only send observations.
  • browser-use: if you want the browser-use Python SDK agent interface, see Using Morph with browser-use.