---
title: "Get signed URLs for the browser state a plan run's tests share"
method: GET
path: "/planRuns/{plan_run_id}/browserState"
tags: ["PlanRun"]
---

# Get signed URLs for the browser state a plan run's tests share

`GET /planRuns/{plan_run_id}/browserState`

Returns the signed URLs a test runner needs to read and write browser state that the
tests of one plan run share.

The state belongs to the plan run, and there is one per session scope — the application
origin plus the credentials. A flow plays no part in addressing it, and deliberately so:
the session is what decides reuse. A test that finds state for its session starts from it,
without comparing its marked flows against whatever the producing test had marked.

A marked flow still runs every time. It is not skipped, so it must decide for itself
whether there is anything left to do — a login flow, for example, wraps its sign-in steps
in a condition that only holds when the login page is actually showing. Leaving that
judgment to the flow is what makes reuse safe: a test can never miss a flow's effect
because some other test produced the state.

`signed_upload_url` is always present. `signed_download_url` is present only when state has
already been stored for this plan run and session scope, so its absence tells a runner to
run the flow itself. Runners that start together can all see no download URL and all
produce, and the write is unconditional, so the last one to upload wins — deliberately,
since a runner that finds an expired session has to be able to replace it. Answering both
questions in one response keeps a miss to the same number of round trips as a hit.

The workspace, and therefore the bucket, is derived server-side from the plan run, and the
object key is composed here from the plan run, the application origin and the
credentials. The caller supplies no path and learns no bucket name, so the URLs cannot be
steered at an unrelated object, and state is only ever shared between tests that would have
logged in as the same user against the same application.

The stored state belongs to the plan run and is never addressable by a later one, since
the key carries the plan run ID. It is not deleted when the run ends; it expires with the
workspace's stored test artifacts, and sooner where a shorter rule applies.

## Path parameters

- `plan_run_id` string, required

## Query parameters

- `application_url` string, required
- `credentials_id` string

## Response `200`

The signed URLs for this plan run's stored browser state

- ArtifactUrls — Signed artifact urls for uploading and downloading
  - `signed_download_url` string — signed url to download the artifact
  - `signed_upload_url` string — signed url to upload the artifact

## Other responses

- `400` — Invalid or missing parameter
- `401` — User not authenticated
- `403` — User not authorized
- `404` — Entity not found
- `default` — Unknown error

---

[API](https://skmtc.dev/mabl/apis/mabl-api.md) · [All operations](https://skmtc.dev/mabl/apis/mabl-api/llms.txt) · [OpenAPI document](https://skmtc-service-production.skmtc.workers.dev/v1/apis/mabl/mabl-api/revisions/213a66c2bc25/schema)
