Chainsbrowse guides

Cross-project chains

Run a test in one project and then a test in another, threading captured values forward — sequential, fail-fast, and masked wherever a value is stored.

Most products are not one application. An order placed in the storefront becomes a row in the ops portal, a line in the finance export and an email. A test that stops at the storefront's confirmation page proves the first hop and nothing after it.

A chain is an ordered list of segments, each naming an existing test in some project. It runs them one after another, in one session of work, carrying values captured in one segment into the next. Chains are org-level rather than project-level, because spanning projects is the entire point.

Chains are a Team plan feature.

If what you need is "the Customer submits and the Manager approves", you want actor scenarios instead — a scenario is a chain that knows who is acting, and re-establishes a different session per stage. A chain is the simpler shape: a line of tests, one after another.

A segment is a reference, not a copy

Each segment carries a project, a test in that project, and two optional overrides:

FieldWhat it does
ProjectWhich project the segment's test lives in.
TestThe test to run. Must belong to that project.
EnvironmentA named environment profile on that project. Unset means the project's defaults.
RoleAn auth role to run as, overriding whatever the test itself is configured with.
DescriptionA note for whoever reads the chain later.

Nothing is copied. Edit the test and every chain referencing it changes with it — that is why a chain is cheaper than a long test that duplicates four flows.

The role resolves in that order: the segment's override wins, then the test's own configured role, then the project's default. The environment supplies that segment's base URL, variables, auth config and setup fixtures, layered over the project's defaults. There is one level of layering — an environment does not inherit from another environment.

Sequential, and fail-fast

Segments run strictly one after another. The first segment that does not pass halts the chain, and every remaining segment is recorded as skipped.

Skipped is recorded, not dropped. A chain that stopped at segment two must not look like a two-segment chain that passed.

A segment that cannot even be resolved fails the same way, with the reason attached rather than an exception: the project is gone, the test is gone, the test belongs to a different project than the segment claims, the named environment does not exist, or the test is not currently runnable (awaiting review, or quarantined). You find out which one, on the segment it happened to.

Cancellation is tracked separately from failure. A chain that was stopped reports cancelled, and the segment that was mid-flight reports cancelled too — not failed. Nothing went wrong; somebody stopped it.

Threading values between segments

This is what makes a chain more than "run these four tests in order". A segment can capture a value — an order id, a reference number, a payout id — and later segments use it as {{token}}.

Each segment's variable map is built from three sources, later winning:

  1. The project's (and environment's) variables.
  2. The chain's own variables, edited on the chain.
  3. Everything captured by earlier segments.

So a chain variable overrides a project default, and a value captured at runtime overrides both.

extract

Captures something from the page into a variable.

FieldMeaning
Sourcetext, attribute, value (an input's current value) or url (the page's URL)
SelectorThe element, for every source but url
AttributeWhich attribute, when the source is attribute
PatternAn optional regex — the first capture group if there is one, otherwise the whole match
Save asThe variable name later steps and segments use
SensitiveMarks the value for masking (see below)

If the element is not there, or the pattern does not match, the step fails. It does not save an empty string and carry on — a variable that silently became "" would fail three segments later, somewhere unrelated.

api-request

Makes an HTTP request and pulls values out of the JSON response.

It goes through the run's browser context, which is the detail that matters: the active role's captured session cookies and Basic Auth apply automatically, so you do not re-authenticate to read your own API. The URL may be absolute or relative to the run's base URL, and is SSRF-checked exactly like a goto.

Values come out by JSON dot-path (data.order.id); a path that is not in the response fails the step. expectStatus pins the status you expect — leave it unset and any 2xx passes.

Set retries (up to five) and backoffMs (default 1000 ms, doubled each attempt) when the endpoint is eventually-consistent. The wait honours a Retry-After header when the server asks for longer than the computed backoff, and no single wait exceeds 30 seconds. A retry happens only on 429, a 5xx, or a network failure — a 401 or a 404 fails immediately, because repeating a request the server already answered definitively just gets the same answer more slowly.

Both actions work in ordinary tests too. They are not chain-only.

Sensitive values are masked at rest

Mark a capture sensitive and the value is written as *** everywhere it is stored or displayed — the step result, the chain run's variable snapshot, the variables strip on the run page. It stays real in memory for the rest of that test, so later steps in the same segment substitute the true value.

It does not stay real across a segment boundary. The cross-segment accumulator is built from each run's masked variables, so a sensitive capture arrives at the next segment as the literal string ***. That is deliberate for now: chains are built to carry non-sensitive identifiers between projects, and per-segment authentication is meant to come from each project's role sessions rather than a token forwarded down the line. If a downstream segment needs a real secret, give that project a role.

Running one

Run chain starts it. A chain is the longest-running thing in Vera — several browser runs in sequence — so expect minutes, not seconds.

While it runs, the page streams progress: each segment starting, per-step progress inside the segment currently executing, each segment's result as it completes, and the finished chain run at the end. Every past run is in the history list below, with its status, duration and the segment breakdown; open any segment that produced a run to read its full report.

The Variables captured strip shows the chain's final variable bag. Masked values read *** there, which is the honest rendering rather than a missing row.

Stopping one

The Stop button appears once the live stream has named the run — a button that could only 404 is worse than no button. A chain running in a worker or in somebody else's tab gets its own Stop in the history list.

A stop is real, and it lands in two places. The chain checks between segments, so nothing further starts. It also forwards the stop into the run that is currently executing, so that segment ends at its next step rather than finishing the whole test first. Everything after it is recorded skipped, and the chain lands cancelled.

Stopping is never plan-gated. An entitlement decides whether you may start work, not whether you may stop it.

Limits worth knowing

  • Team plan and above. On Free or Pro, the chain routes answer with a plan prompt.
  • A chain needs at least one segment. There is no maximum, which means a forty-segment chain is possible and is almost certainly the wrong shape — fail-fast means the last segment only ever runs when the other thirty-nine passed.
  • When a chain is queued to a worker, the resolved auth is stripped before the job is enqueued. Captured sessions do not sit in the job table.
  • From an MCP client, an agent can list chains, start one (it returns the run id immediately rather than waiting), poll the chain run, and stop it with cancel_run and kind: "chain".

See also