
How do I use MultiOn sessions so my agent can continue a workflow using the same session_id across multiple calls?
Most teams hit the same wall the first time they try to move from “single-shot” web actions to real workflows: they realize they need the exact same browser session to stay alive across multiple API calls. That’s what MultiOn sessions are for, and the session_id is the handle you use to keep that workflow continuous.
This guide walks through how to use MultiOn sessions so your agent can continue a workflow using the same session_id across multiple calls, with concrete examples for Amazon checkout, posting on X, and extracting a product catalog.
Why sessions matter for real workflows
If you’re coming from Playwright/Selenium, you already know the failure pattern:
- Login happens in one place.
- The next step runs in a fresh browser, so the login cookie is gone.
- Bot protection flares up, or you get bounced to the homepage.
MultiOn’s Sessions + Step mode avoids that by:
- Running your workflow in a secure remote session.
- Giving you a
session_idyou can reuse across calls. - Letting you “step” through a task: navigate, act, verify, then continue.
The mental model is:
Intent in (
cmd+url) → actions executed in a real browser →session_idout → reusesession_idto continue the same browser session.
The core pattern: create, reuse, close
At the API level, using MultiOn sessions with a consistent session_id is a three-step pattern:
-
Create a session
- Send a
POST https://api.multion.ai/v1/web/browsewith your initialcmdandurl. - MultiOn spins up a secure remote browser session and returns a
session_id.
- Send a
-
Reuse the same
session_idacross multiple calls- For every subsequent step in the workflow, call the same endpoint.
- Include the same
session_idin the request body to continue in that exact browser session.
-
Optionally close or let the session expire
- When you’re done (checkout completed, post published), you can drop the
session_idor call a cleanup endpoint if exposed in your environment. - MultiOn handles session timeout according to platform limits (treat it like any remote browser lease).
- When you’re done (checkout completed, post published), you can drop the
Everything else—login persistence, cart state, CSRF tokens, dynamic UI—is handled inside that remote session.
Minimal call shape with session_id
Here’s the smallest useful shape for using sessions via the Agent API (V1 Beta).
First call: start a new session
curl -X POST https://api.multion.ai/v1/web/browse \
-H "Content-Type: application/json" \
-H "X_MULTION_API_KEY: $MULTION_API_KEY" \
-d '{
"url": "https://www.amazon.com/",
"cmd": "Search for a Nintendo Switch OLED and open the product page of the top result."
}'
What to look for in the response:
- A
session_idfield (string) — this is your session handle. - A description of what the agent did (e.g., navigation, clicks).
- Optional screenshot and/or DOM snippet depending on your integration.
Example (simplified):
{
"session_id": "sess_91db8e7a8e0344c5b0c3fad2e6e4b317",
"status": "success",
"result": {
"description": "Opened the product page for 'Nintendo Switch OLED' on Amazon.",
"url": "https://www.amazon.com/dp/XXXXXXXXX"
}
}
Save session_id somewhere you control (DB row, cache, or in-memory if it’s a short-lived backend call chain).
Subsequent calls: continue the same session
On the next step, you don’t want a new browser. You want to continue:
curl -X POST https://api.multion.ai/v1/web/browse \
-H "Content-Type: application/json" \
-H "X_MULTION_API_KEY: $MULTION_API_KEY" \
-d '{
"session_id": "sess_91db8e7a8e0344c5b0c3fad2e6e4b317",
"cmd": "Add this item to cart, then proceed to checkout until the payment page."
}'
Because you passed the same session_id, the agent:
- Uses the same logged-in state.
- Uses the same cart.
- Stays inside the same secure remote session.
You can keep repeating this pattern, adjusting the cmd each time while reusing that session_id.
Example 1: Multi-step Amazon checkout with a shared session_id
Let’s walk a realistic flow built on top of Sessions + Step mode.
Step 1: Login and navigate to the product
curl -X POST https://api.multion.ai/v1/web/browse \
-H "Content-Type: application/json" \
-H "X_MULTION_API_KEY: $MULTION_API_KEY" \
-d '{
"url": "https://www.amazon.com/",
"cmd": "Log in using the saved browser profile if available, search for 'AirPods Pro', and open the first product page."
}'
Your backend does:
- Parse the
session_idfrom the response. - Persist it to your “task run” or “order attempt” record.
Step 2: Add to cart in the same session
curl -X POST https://api.multion.ai/v1/web/browse \
-H "Content-Type: application/json" \
-H "X_MULTION_API_KEY: $MULTION_API_KEY" \
-d '{
"session_id": "sess_91db8e7a8e0344c5b0c3fad2e6e4b317",
"cmd": "Add this item to the cart, then navigate to the cart page."
}'
The agent does:
- A click on “Add to Cart”.
- Follows the UI flow to the cart page.
- Returns you updated state (URL, description, maybe a summary of cart contents).
Step 3: Proceed to checkout and stop at payment
curl -X POST https://api.multion.ai/v1/web/browse \
-H "Content-Type: application/json" \
-H "X_MULTION_API_KEY: $MULTION_API_KEY" \
-d '{
"session_id": "sess_91db8e7a8e0344c5b0c3fad2e6e4b317",
"cmd": "Proceed to checkout, confirm the existing shipping address, and stop when you reach the payment selection page. Do not place the order."
}'
Because all steps share the same session_id, you don’t fight:
- Lost login state.
- New-device challenges every step.
- Cart desyncs.
From a platform perspective, “session continuity” is your real SLA; keeping the session_id consistent is how you get it.
Example 2: Posting on X with two-step confirmation
Another common pattern is API → human approval → API.
- Agent drafts the post in X with a
session_id. - You present a preview to the user.
- If they approve, you reuse the same
session_idto click “Post”.
Step 1: Draft a post
curl -X POST https://api.multion.ai/v1/web/browse \
-H "Content-Type: application/json" \
-H "X_MULTION_API_KEY: $MULTION_API_KEY" \
-d '{
"url": "https://x.com/",
"cmd": "Log in if necessary and draft a new post saying: \"Launching our new feature today!\" but do not publish yet."
}'
Store the returned session_id. Show the user a screenshot/preview.
Step 2: Publish in the same session after approval
curl -X POST https://api.multion.ai/v1/web/browse \
-H "Content-Type: application/json" \
-H "X_MULTION_API_KEY: $MULTION_API_KEY" \
-d '{
"session_id": "sess_xxx_approved",
"cmd": "Click the button to publish the drafted post."
}'
The same session_id means:
- The draft is still loaded in that browser tab.
- The user is still logged in.
- No re-auth or “you’re posting from a new device” prompts.
Example 3: Extract, then act (sessions + Retrieve)
Sometimes you need structured JSON out and actions in the same workflow. The pattern is:
- Use the Agent API to navigate in a session.
- Use Retrieve (with the same session context) to turn the dynamic page into a JSON array of objects.
- Optionally act again in the same
session_id.
Assume you used POST /v1/web/browse to reach an H&M category page in a session:
# Step 1: navigate via Agent API
curl -X POST https://api.multion.ai/v1/web/browse \
-H "Content-Type: application/json" \
-H "X_MULTION_API_KEY: $MULTION_API_KEY" \
-d '{
"url": "https://www2.hm.com/en_us/men/products/jeans.html",
"cmd": "Open the H&M men jeans category page and scroll to load products."
}'
You get:
{
"session_id": "sess_hm_jeans_123",
"status": "success",
"result": { "url": "https://www2.hm.com/en_us/men/products/jeans.html" }
}
Step 2: Retrieve structured JSON using the same session
Retrieve is designed to produce JSON arrays of objects and handle dynamic pages with controls like renderJs, scrollToBottom, and maxItems.
Example call shape (your environment may expose this as POST https://api.multion.ai/v1/web/retrieve):
curl -X POST https://api.multion.ai/v1/web/retrieve \
-H "Content-Type: application/json" \
-H "X_MULTION_API_KEY: $MULTION_API_KEY" \
-d '{
"session_id": "sess_hm_jeans_123",
"renderJs": true,
"scrollToBottom": true,
"maxItems": 50,
"schema": {
"type": "array",
"items": {
"type": "object",
"properties": {
"name": { "type": "string" },
"price": { "type": "string" },
"url": { "type": "string" },
"image": { "type": "string" }
},
"required": ["name", "price", "url"]
}
}
}'
Return shape (simplified):
[
{
"name": "Slim Fit Jeans",
"price": "$39.99",
"url": "https://www2.hm.com/en_us/productpage.1234567890.html",
"image": "https://image.hm.com/assets/hm1234567890.jpg"
},
{ "...": "..." }
]
Because Retrieve uses the same session_id, you inherit:
- Any prior scrolling the agent has done.
- Any location/locale selection.
- Any login-based personalization, if relevant.
You can then pass that session_id back into web/browse again to act on a specific product.
Handling errors and session expiry
In a production integration, treat session_id like any other remote resource lease:
- Check for 4xx/5xx responses on every call.
- If you see
402 Payment Required, your usage or billing state needs attention before you can continue that session.
- If you see
- Handle “session not found” or expiry gracefully.
- Implement a retry policy that can:
- Start a new session.
- Re-run critical steps (e.g., re-login, navigate back to the target page).
- Implement a retry policy that can:
- Store session metadata:
session_id- Creation time
- Last-used time
- Associated user / workflow run
This makes it trivial to decide when to reuse, when to let expire, and when to spawn a new parallel agent.
Using Sessions + Step mode from your app code
Here’s how this looks in a Node backend, with a minimal “orchestrator” that keeps the same session_id across steps.
import fetch from "node-fetch";
const MULTION_API_KEY = process.env.MULTION_API_KEY;
const BASE_URL = "https://api.multion.ai/v1/web/browse";
async function multionStep(payload: any) {
const res = await fetch(BASE_URL, {
method: "POST",
headers: {
"Content-Type": "application/json",
"X_MULTION_API_KEY": MULTION_API_KEY!
},
body: JSON.stringify(payload)
});
if (res.status === 402) {
throw new Error("MultiOn: 402 Payment Required – check billing/quotas.");
}
if (!res.ok) {
const text = await res.text();
throw new Error(`MultiOn error ${res.status}: ${text}`);
}
return res.json();
}
export async function runAmazonFlow() {
// Step 1: start session
const first = await multionStep({
url: "https://www.amazon.com/",
cmd: "Search for 'Nintendo Switch OLED' and open the top product page."
});
const sessionId = first.session_id;
if (!sessionId) throw new Error("Missing session_id in first MultiOn response");
// Step 2: continue with same session
const second = await multionStep({
session_id: sessionId,
cmd: "Add this item to cart and go to the cart page."
});
// Step 3: another step with the same session
const third = await multionStep({
session_id: sessionId,
cmd: "Proceed to checkout until the payment method selection page, but do not place the order."
});
return { first, second, third };
}
Key points:
- Only the first call supplies
url; subsequent calls rely on the current page in that session. - Every later call includes
session_idand a newcmd. - Errors include payment state (e.g.,
402 Payment Required) as first-class signals.
Parallel agents with distinct session_ids
Sessions become even more powerful when you scale out:
- You can have many concurrent
session_ids, each representing an independent browser session. - Each session can:
- Log into a different account.
- Be assigned to a different user or tenant.
- Run the same workflow template with different parameters.
Pattern:
- Per user: allocate a new
session_idfor long-running tasks, store it on their profile, reuse until idle. - Per job: allocate a new
session_idper batch (e.g., 50 product checks), reuse inside that batch, then let it expire.
This is how you get “infinite scalability with parallel agents”: each session_id is a unit of concurrency, and MultiOn runs those as secure remote sessions behind the scenes.
Practical guidelines for using the same session_id across calls
From years of babysitting brittle Playwright/Selenium flows, this is the operational checklist I’d actually enforce:
-
Always persist
session_idon first response
Don’t rely on an in-function variable; store it in DB/cache tagged to the workflow or user. -
Pass
session_idon every subsequent call
If you forget it once, you’re in a new browser. That’s a silent failure mode if you’re not logging it. -
Treat sessions as stateful resources
Trackcreated_atandlast_step_at. If a session is older than your acceptable threshold, consider starting a new one. -
Separate “navigation” from “destructive actions”
Use smallercmds so you can inspect responses between steps:- Step A: get to the page.
- Step B: perform the action. This gives you better control and observability.
-
Log
session_idwith every error
When something goes wrong, the first question should be:
“Is this an agent command issue, or did this session die / get challenged?”
How this replaces brittle Playwright/Selenium flows
With Playwright/Selenium, you’ve probably fought:
- Cookies that don’t persist across workers.
- Manual proxy juggling to appease bot protection.
- Hand-rolled “remote Chrome farms” to keep sessions alive.
MultiOn’s Sessions + Step mode and secure remote sessions give you:
- A stable
session_idinterface instead of hand-managed browser objects. - Native proxy support and infrastructure tuned for “tricky bot protection.”
- An API surface where “intent in, actions executed in a real browser, and structured JSON out” is the default—not an internal project.
You stop maintaining selector jungles and start orchestrating workflows through a small set of primitives: Agent API, Retrieve, session_id, and stepwise cmds.
Next step
If you want to see MultiOn sessions in action in your own stack, start wiring the pattern above into a small workflow—one Amazon-like checkout or one X posting flow—and watch how much friction disappears once you treat session_id as the core unit of continuity.