The v1 API.
Drive simulations and read reports programmatically. Available on Pro and Enterprise. Battle-mode endpoints require Pro+.
Generate a key in Settings → API keys. Copy it once.
Send Authorization: Bearer agl_live_… on every request.
Curl examples for every endpoint below. JSON in, JSON out.
Every request to /api/v1/* must include an Authorization header. Keys start with agl_live_ followed by 24 random URL-safe characters. Revoking a key in Settings takes effect immediately — the next request returns 401.
GET/api/v1/whoamiSanity-check the key. Returns the owning account and the plan it's gated to. Useful as a connection test before running anything destructive.
/api/v1/whoamicurl https://api.agentlens.example/api/v1/whoami \ -H "Authorization: Bearer agl_live_xxxxxxxxxxxxxxxxxxxxxxxx"
{
"account_id": "3c35c1ba-0e7f-4c63-9e83-acbb…",
"account_name": "ACME UX team",
"plan": "pro",
"credits_remaining": 84532
}Launch runs, list/inspect them, cancel mid-flight, edit tags, poll progress.
POST/api/v1/simulationsQueue a new simulation. The job lands on your account's queue (priority queue if you're on Pro+). Returns the new simulation id and the estimated max credits — the actual spend is capped by credit_budget.
/api/v1/simulationscurl https://api.agentlens.example/api/v1/simulations \
-H "Authorization: Bearer agl_live_xxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{
"website_url": "https://acme.example.com",
"name": "checkout flow — pricing change",
"mode": "task",
"objective": "Find the pricing page and complete checkout",
"agent_count": 20,
"max_steps_per_agent": 12,
"max_pages_per_agent": 10,
"tags": ["pricing-v2", "Q2"]
}'{
"simulation_id": "1f3a-…",
"status": "queued",
"estimated_max_credits": 200
}GET/api/v1/simulationsList simulations for the account, newest first. Filter by status and tag. Respects the plan's run-history retention window — older runs return 410 individually but are silently omitted from the list.
/api/v1/simulationscurl "https://api.agentlens.example/api/v1/simulations?limit=50&status=completed&tag=pricing-v2" \ -H "Authorization: Bearer agl_live_xxxxxxxxxxxxxxxxxxxxxxxx"
[
{
"id": "1f3a-…",
"name": "checkout flow — pricing change",
"status": "completed",
"agent_count": 20,
"credits_used": 184,
"credit_budget": 200,
"tags": ["pricing-v2", "Q2"],
"started_at": "2026-05-19T14:02:11",
"completed_at": "2026-05-19T14:21:48",
"...": "…"
}
]GET/api/v1/simulations/{simulation_id}Fetch a single simulation. Returns 410 if the run is outside your plan's retention window — upgrade to read it again.
/api/v1/simulations/{simulation_id}curl https://api.agentlens.example/api/v1/simulations/1f3a-… \ -H "Authorization: Bearer agl_live_xxxxxxxxxxxxxxxxxxxxxxxx"
POST/api/v1/simulations/{simulation_id}/cancelRequest cancellation. The status transitions to 'canceling' immediately; workers tear down on the next checkpoint. Already-terminal runs return a noop.
/api/v1/simulations/{simulation_id}/cancelcurl -X POST https://api.agentlens.example/api/v1/simulations/1f3a-…/cancel \ -H "Authorization: Bearer agl_live_xxxxxxxxxxxxxxxxxxxxxxxx"
{ "status": "canceling" }PATCH/api/v1/simulations/{simulation_id}/tagsReplace the tag set. Whitespace is stripped and duplicates removed server-side — send the full desired set on every call.
/api/v1/simulations/{simulation_id}/tagscurl -X PATCH https://api.agentlens.example/api/v1/simulations/1f3a-…/tags \
-H "Authorization: Bearer agl_live_xxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{ "tags": ["pricing-v2", "Q2", "regression"] }'GET/api/v1/simulations/{simulation_id}/progressPoll-style status snapshot. Cheaper than the SSE stream — call this on a 2-5s loop while a run is active. agents_completed counts every terminal status (success, stuck, abandoned, max-steps, failed, canceled).
/api/v1/simulations/{simulation_id}/progresscurl https://api.agentlens.example/api/v1/simulations/1f3a-…/progress \ -H "Authorization: Bearer agl_live_xxxxxxxxxxxxxxxxxxxxxxxx"
{
"simulation_id": "1f3a-…",
"status": "running",
"agent_count": 20,
"agents_completed": 14,
"agents_by_status": {
"running": 6,
"goal_reached": 9,
"stuck": 3,
"max_steps_reached": 2
},
"credits_used": 132,
"credit_budget": 200,
"started_at": "2026-05-19T14:02:11",
"completed_at": null
}Once a run completes, pull the analysis. Reports come as either structured JSON or rendered Markdown.
GET/api/v1/simulations/{simulation_id}/reportThe full structured report — executive summary, headline metrics, funnel, page-level insights, persona insights, recommendations, success/failure paths. Returns 404 until the report-generation job has finished.
/api/v1/simulations/{simulation_id}/reportcurl https://api.agentlens.example/api/v1/simulations/1f3a-…/report \ -H "Authorization: Bearer agl_live_xxxxxxxxxxxxxxxxxxxxxxxx"
{
"id": "report-…",
"simulation_id": "1f3a-…",
"status": "ready",
"executive_summary": "62% of agents completed checkout…",
"metrics": {
"completion_rate": 0.62,
"average_confusion": 0.31,
"total_agents": 20
},
"funnel": [ { "url": "…", "agents_reached": 20 }, … ],
"page_insights": [ … ],
"persona_insights": [ … ],
"recommendations": [ … ]
}GET/api/v1/simulations/{simulation_id}/report/markdownThe same report rendered as Markdown (text/markdown content-type). Convenient for piping into a static-site generator or wiki page.
/api/v1/simulations/{simulation_id}/report/markdowncurl https://api.agentlens.example/api/v1/simulations/1f3a-…/report/markdown \ -H "Authorization: Bearer agl_live_xxxxxxxxxxxxxxxxxxxxxxxx"
GET/api/v1/simulations/{simulation_id}/pagesPer-URL aggregates: visit count, unique agent count, average confusion/attraction/trust, dropoff and goal-success counts, plus a representative screenshot key (use the /files endpoint to fetch the bytes).
/api/v1/simulations/{simulation_id}/pagescurl https://api.agentlens.example/api/v1/simulations/1f3a-…/pages \ -H "Authorization: Bearer agl_live_xxxxxxxxxxxxxxxxxxxxxxxx"
GET/api/v1/simulations/{simulation_id}/agentsList agents for a simulation. Supports filtering by status and any persona dimension (confidence, technical_ability, background, motivation, accessibility, device, geography, purchase_behavior). Paginate with limit + offset.
/api/v1/simulations/{simulation_id}/agentscurl "https://api.agentlens.example/api/v1/simulations/1f3a-…/agents?status=goal_reached&persona_dimension=device&persona_value=mobile&limit=50" \ -H "Authorization: Bearer agl_live_xxxxxxxxxxxxxxxxxxxxxxxx"
GET/api/v1/agents/{agent_id}Single agent with the full ordered step list. Each step carries observation, thought summary, action, result, emotional state, and goal assessment — everything you need to reconstruct the run.
/api/v1/agents/{agent_id}curl https://api.agentlens.example/api/v1/agents/9ab2-… \ -H "Authorization: Bearer agl_live_xxxxxxxxxxxxxxxxxxxxxxxx"
GET/api/v1/agents/{agent_id}/trajectoryA rendered Markdown trajectory for the agent (text/markdown). Returns 404 if the trajectory file hasn't been generated yet.
/api/v1/agents/{agent_id}/trajectorycurl https://api.agentlens.example/api/v1/agents/9ab2-…/trajectory \ -H "Authorization: Bearer agl_live_xxxxxxxxxxxxxxxxxxxxxxxx"
Per-page click / attention / confusion / attraction / repulsion / hesitation overlays.
GET/api/v1/simulations/{simulation_id}/heatmapsList every page that has heatmap coverage, ordered by visit count. Returns the page id you need for the detail call, plus the representative screenshot key.
/api/v1/simulations/{simulation_id}/heatmapscurl https://api.agentlens.example/api/v1/simulations/1f3a-…/heatmaps \ -H "Authorization: Bearer agl_live_xxxxxxxxxxxxxxxxxxxxxxxx"
GET/api/v1/simulations/{simulation_id}/heatmaps/{page_id}The actual heatmap points for a page. Filter by type (click | attention | confusion | attraction | repulsion | hesitation), outcome (success | failure), and any persona dimension. Hard-capped at 5 000 points per response.
/api/v1/simulations/{simulation_id}/heatmaps/{page_id}type (click | attention | confusion | attraction | repulsion | hesitation), outcome (success | failure), and any persona dimension. Hard-capped at 5 000 points per response.curl "https://api.agentlens.example/api/v1/simulations/1f3a-…/heatmaps/page-…?type=confusion&outcome=failure&limit=500" \ -H "Authorization: Bearer agl_live_xxxxxxxxxxxxxxxxxxxxxxxx"
Screenshots, trajectory Markdown, report JSON/Markdown — anything referenced by an object_key on the rows above.
GET/api/v1/files/{key}Stream a stored object. The key is the path you see on rows (e.g. screenshot_object_key, trajectory_object_key, representative_screenshot_key). Keys that no row in your account references will 404 — even if the file physically exists — so cross-tenant access by guessing UUIDs is impossible.
/api/v1/files/{key}curl https://api.agentlens.example/api/v1/files/screenshots/1f3a-…/step-007.png \ -H "Authorization: Bearer agl_live_xxxxxxxxxxxxxxxxxxxxxxxx" \ --output step-007.png
Group N simulations into a head-to-head and compare. Pro and Enterprise only.
POST/api/v1/battlesCreate a battle group. Pick a primary_metric — completion_rate (default), average_confusion, total_agents, average_attraction, etc. — that's the metric the verdict line will summarize.
/api/v1/battlescurl https://api.agentlens.example/api/v1/battles \
-H "Authorization: Bearer agl_live_xxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{
"name": "/checkout — v1 vs v2",
"hypothesis": "Sticky CTA improves completion by ≥5pp",
"primary_metric": "completion_rate"
}'GET/api/v1/battlesList every battle on this account, newest first, each with its current entries.
/api/v1/battlescurl https://api.agentlens.example/api/v1/battles \ -H "Authorization: Bearer agl_live_xxxxxxxxxxxxxxxxxxxxxxxx"
GET/api/v1/battles/{battle_id}Fetch a single battle + its entries (no comparison math).
/api/v1/battles/{battle_id}curl https://api.agentlens.example/api/v1/battles/b-… \ -H "Authorization: Bearer agl_live_xxxxxxxxxxxxxxxxxxxxxxxx"
DELETE/api/v1/battles/{battle_id}Permanently remove the battle and all its entries. The underlying simulations are not deleted.
/api/v1/battles/{battle_id}curl -X DELETE https://api.agentlens.example/api/v1/battles/b-… \ -H "Authorization: Bearer agl_live_xxxxxxxxxxxxxxxxxxxxxxxx"
POST/api/v1/battles/{battle_id}/entriesAdd a simulation to a battle. Mark one entry as is_baseline — comparisons compute deltas against that one. Adding a second baseline silently demotes the first; adding the same sim twice returns 409.
/api/v1/battles/{battle_id}/entriesis_baseline — comparisons compute deltas against that one. Adding a second baseline silently demotes the first; adding the same sim twice returns 409.curl https://api.agentlens.example/api/v1/battles/b-…/entries \
-H "Authorization: Bearer agl_live_xxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{
"simulation_id": "1f3a-…",
"label": "v1 (control)",
"is_baseline": true,
"change_summary": "Original pricing page"
}'PATCH/api/v1/battles/{battle_id}/entries/{entry_id}Update an entry's label, change summary, ordinal, or baseline flag.
/api/v1/battles/{battle_id}/entries/{entry_id}curl -X PATCH https://api.agentlens.example/api/v1/battles/b-…/entries/e-… \
-H "Authorization: Bearer agl_live_xxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{ "label": "v2 — sticky CTA", "change_summary": "Sticky CTA + lighter headline" }'DELETE/api/v1/battles/{battle_id}/entries/{entry_id}Remove an entry from a battle. The simulation itself stays.
/api/v1/battles/{battle_id}/entries/{entry_id}curl -X DELETE https://api.agentlens.example/api/v1/battles/b-…/entries/e-… \ -H "Authorization: Bearer agl_live_xxxxxxxxxxxxxxxxxxxxxxxx"
GET/api/v1/battles/{battle_id}/compareThe big one. Returns side-by-side KPIs for every entry (with deltas vs. the baseline), the top 6 persona buckets where the variant won, the top 6 where it lost, per-URL drop-off and confusion deltas, and a one-line verdict.
/api/v1/battles/{battle_id}/comparecurl https://api.agentlens.example/api/v1/battles/b-…/compare \ -H "Authorization: Bearer agl_live_xxxxxxxxxxxxxxxxxxxxxxxx"
{
"battle": { "id": "b-…", "name": "/checkout — v1 vs v2", … },
"entries": [
{
"label": "v1 (control)",
"is_baseline": true,
"metrics": { "completion_rate": 0.58, "average_confusion": 0.34 },
"deltas": { "completion_rate": 0.0, "average_confusion": 0.0 }
},
{
"label": "v2 — sticky CTA",
"is_baseline": false,
"metrics": { "completion_rate": 0.71, "average_confusion": 0.28 },
"deltas": { "completion_rate": 0.13, "average_confusion": -0.06 }
}
],
"persona_winners": [ { "dimension": "device", "value": "mobile", "delta": 18.4, … } ],
"persona_losers": [ { "dimension": "confidence", "value": "low", "delta": -4.1, … } ],
"page_deltas": [ { "normalized_url": "/checkout", "delta_confusion": -0.12, … } ],
"verdict": "v2 — sticky CTA beats v1 (control) by +13.0pp on completion rate."
}GET/api/v1/battles/{battle_id}/snapshots?normalized_url=…Raw HTML snapshots of the same URL across every entry — feed these into your own diff tool. Entries that didn't capture this URL come back with html: null instead of an error.
/api/v1/battles/{battle_id}/snapshots?normalized_url=…curl "https://api.agentlens.example/api/v1/battles/b-…/snapshots?normalized_url=acme.example.com/checkout" \ -H "Authorization: Bearer agl_live_xxxxxxxxxxxxxxxxxxxxxxxx"
400The request body / query is invalid. The detail field tells you which field is wrong.401Missing or malformed Authorization header, or the key was revoked.402Not enough credits to cover the requested simulation budget. Buy a pack or wait for the next period reset.403The key is valid, but the account's plan doesn't include this feature (API access — Hobby/Plus; or battle mode — Hobby/Plus).404The resource doesn't exist or isn't owned by this account. We don't distinguish — that would leak existence across tenants.409Add-entry conflict: this simulation is already in the battle.410Simulation falls outside this plan's run-history retention window. The row still exists — upgrade to read it again.422Custom personas failed validation. The detail.report field has per-file errors.What you'll actually hit before anything else.
We don't enforce request-rate limits in MVP. The hard cap is credit consumption — the API draws from the same pool as the dashboard, so a runaway script with 8000 credits/month on Pro can burn through your quota in a single afternoon. Pro+ accounts route onto a dedicated arq:priority queue, so an API-launched run skips ahead of Plus traffic on the shared workers.