Agent framework integration

Twin Browser + LangGraph

LangGraph is LangChain’s low-level orchestration library: you build an explicit state graph of nodes and edges, compile it, and invoke it. Nodes are plain functions over the graph state, so a node does not have to involve a model at all.

Agent framework4-step setupBearer key auth

How Twin plugs into LangGraph

This is the difference that matters in LangGraph: Twin does not have to be a tool the model chooses — it can be a NODE the graph always runs. A node is a function, so the browser step becomes a deterministic edge in your graph with a known cost, sitting outside the model loop entirely. Point it at `/api/v1/skills/{name}/run` once the flow is compiled and that node makes no model call at all; keep it on `/api/v1/run` while the flow is still being figured out.

Twin is the browser execution layer your stack calls. The first run cold-compiles a skill via skill compilation; every similar request after that is matched from the cache and replayed deterministically, so your marginal cost per run trends toward zero rather than climbing with usage.

app.example.com
  1. Receive goal from LangGraphdone
  2. Compile DOM → token-efficient indexed statedone
  3. Match the semantic dispatch cacherunning
  4. Replay compiled skill — 0 LLM callsqueued

Wire it up

Drop Twin into LangGraph.

Copy, paste, and swap in your Bearer key. The first run compiles a skill; repeats match the semantic dispatch cache and replay deterministically.

LangGraph — Twin as a graph nodepython
import os, requests
from langgraph.graph import StateGraph, MessagesState, START, END

TWIN = "https://twin-browser.com/api/v1"

def browser_step(state: MessagesState):
    """A deterministic node: no model call, one browser task."""
    r = requests.post(
        f"{TWIN}/run",
        headers={"Authorization": f"Bearer {os.environ['TWIN_API_KEY']}"},
        json={
            "url": "https://app.acme.com/billing",
            "prompt": "Sign in and download the latest invoice as CSV",
            "success": {"kind": "textVisible", "value": "Invoice"},
        },
        timeout=300,
    ).json()
    return {"messages": [{"role": "ai", "content": str(r.get("result"))}]}

graph = StateGraph(MessagesState)
graph.add_node(browser_step)
graph.add_edge(START, "browser_step")
graph.add_edge("browser_step", END)
app = graph.compile()

Base URL https://twin-browser.com/api/v1 · auth Authorization: Bearer ab_live_… · over MCP the same engine is run_goal, compile_skill and run_skill the full tool table.

  1. Install LangGraph

    pip install langgraph. Nothing Twin-specific is installed.

  2. Write the node

    A node is a function over state — have it POST to /api/v1/run and write the result back into state.

  3. Wire the graph

    add_node, add_edge from START, and compile. The browser step now runs deterministically in the graph.

  4. Swap in a replay

    Once the goal is compiled, point the node at /api/v1/skills/{name}/run so it costs no model call.

FAQ

LangGraph on Twin — common questions

Should Twin be a node or a tool in LangGraph?
A node when you already know the browser step has to happen — it keeps the step out of the model loop and gives it a fixed cost. A tool when the model should decide whether the web is needed at all. Both are the same REST call underneath.
What happens if the run hits a 2FA wall inside a graph?
A sign-in run parks by default rather than failing: the response is { status: "paused", sessionId } and the run can be resumed once a human supplies the code or approves out of band. Handle that as its own branch in the graph rather than as an error.

Wire up LangGraph in minutes.

Free to start. Usage-based credits from $29/mo — each call billed the higher of its flat action price or its metered cost.