Reports
A terminal summary is enough while you write a test. A team needs more: what each step looked like, which step failed and why, and a link to send. A report is one run of your cases kept in that form.
There are two parts. The open-source CLI writes a run to a directory on your machine. The hosted service at lynkeus.io turns that directory into a page anyone with the link can open. The first part works on its own. The second is the paid service, which has a free plan.
Before you start
Section titled “Before you start”- You have cases that run. See Writing cases.
- For screenshots: a host that can take them. A simulator or an emulator can. The headless host renders nothing to capture.
- For the hosted report: an API key from lynkeus.io.
Step 1: Write the run to a directory
Section titled “Step 1: Write the run to a directory”npx lynkeus case run --out run --screenshots--out <dir> writes the run as a directory. --screenshots adds a frame of the screen after every app step. Both --screenshots and --video need --out, since that is where they keep what they take.
You should see the usual output in the terminal, and afterward this on disk:
run/ run.json when it started and ended, what the app ran on, the totals cases.jsonl one line per attempt of a case, in the order they ran login/ one folder per case 01-app-press-next-button.png 01-app-press-next-button.json … login.2/ the second attempt, when the case was retriedEach screenshot has a .json file beside it with the screen’s structure at that moment.
Add --video to film each case, where the host can film. Each step then records the second of the video at which it began. --video-fps <n> sets the frame rate (30 by default).
Step 2: Read the run directory
Section titled “Step 2: Read the run directory”You do not need the hosted service to use a run. The directory is a documented format that a script or a dashboard of your own can read.
run.json is the manifest:
{ "schema": 1, "startedAt": "2026-10-12T09:14:03.120Z", "git": { "commit": "4f2a9c1", "branch": "feature/checkout" }, "finishedAt": "2026-10-12T09:16:41.007Z", "platform": "ios", "native": true, "totals": { "cases": 12, "passed": 11, "failed": 1, "unsupported": 0, "retried": 2 }}native says whether the app ran on a device or simulator (true) or was hosted without one. git is present when the run was made from a git checkout. totals counts each case by its last attempt.
Each line of cases.jsonl is one attempt of one case. It carries the case’s id, title, description and file, the attempt (1, or 2 for the second chance), the result, when it started, how long it took, and its steps.
Each step says:
| Field | Meaning |
|---|---|
step |
The step as text |
section |
setup or steps |
status |
passed, failed, skipped or unsupported |
ms |
How long it took |
error |
Why it failed, when it did |
events |
The analytics events the app sent while it ran |
screenshot |
The frame after it, relative to the run directory |
videoS |
The second of the video at which it began |
backend |
What the backend did for it, with --traces |
A failed attempt also keeps its evidence (the screen at the failure, the last requests, and why) and a diagnosis.
The TypeScript types are RunManifest, RunCase, RunStep and RunEvent in the lynkeus-protocol package, with the schema number in RUN_SCHEMA. Fields are only ever added. A change to what an existing field means raises the schema number, and a reader should refuse a schema it does not know.
Step 3: Get a key for the hosted service
Section titled “Step 3: Get a key for the hosted service”- Sign in at lynkeus.io with Google or GitHub. You get an organization of your own, or join the one that invited you.
- Under API keys, create a key. It is shown once.
- Export the service address and the key:
export LYNKEUS_REPORTS_URL=https://lynkeus.ioexport LYNKEUS_REPORTS_KEY=lk_…Step 4: Push the run
Section titled “Step 4: Push the run”Add --push to upload the run when it ends:
npx lynkeus case run --out run --screenshots --pushYou should see the report’s address at the end of the summary:
11/12 passedreport: https://lynkeus.io/r/…newly failing (1): checkout-declined-cardThe lines after the address compare this run to the one before it: newly failing (n): … and fixed since the run before (n): …. They appear only when there is something to say.
A run you already have on disk goes up with report push:
npx lynkeus report push run --project myapp--project <name> is the project the run is filed under. It defaults to the name of the app’s folder.
--push checks the two variables before the run starts, so a missing key stops the command at once instead of after every case has run. If the upload itself fails, the run still counts: the summary says the report was not uploaded: … and the exit code reflects the cases.
What the hosted report shows
Section titled “What the hosted report shows”- Each case, with its steps and how each one ended.
- The screenshot after each step, and the video in step with the steps where one was recorded.
- The analytics events each step sent. See Analytics events.
- Why a failed case failed.
- What the backend did for each step, when the run used
--traces. See Backend traces. - The branch and commit the run was made from.
The report’s address contains a token of its own that cannot be guessed. Anyone with the link can open it.
List reports from the terminal
Section titled “List reports from the terminal”npx lynkeus report list # the organization's latest reportsnpx lynkeus report history checkout-declined-card # how one case came out, run by runnpx lynkeus report flakes --project myapp # cases that do not always pass at the first try (Team)report list prints one line per run: when it started, its project and platform, how many cases passed, and the report’s address. --limit <n> sets how many (20 by default, at most 100).
report history and report flakes are explained in Flaky tests.
Plans and retention
Section titled “Plans and retention”| Free | Team | |
|---|---|---|
| Projects | 1 | Unlimited |
| History kept | 7 days | 90 days |
| Screenshot per step | Yes | Yes |
| Video | No | Yes |
| Events per step | Yes | Yes |
| Flaky-test trends | No | Yes |
Each day, a scheduled job deletes the runs that are older than their plan keeps, with their files.
On the Free plan a video is refused at upload. The rest of the run is kept, and the command says which files were left out:
report: https://lynkeus.io/r/… (not kept on this plan: login/video.mp4)See your plan, or change it:
npx lynkeus report plan # the organization's plan and what it keepsnpx lynkeus report plan --upgrade # prints the Stripe checkout address for Teamnpx lynkeus report plan --manage # prints the Stripe portal: card, invoices, cancelingHow it works
Section titled “How it works”report push sends the run in four stages: it creates the run from run.json, sends cases.jsonl, uploads every screenshot and video the cases name, and marks the run finished. The service answers the last stage with the report’s address and the comparison to the previous run.
The run directory is the only thing the service reads. It does not connect to your app, your simulator or your backend.
Common problems
Section titled “Common problems”--screenshots and --video keep what they take in the run directory: add --out <dir>. Add --out.
--push uploads the run directory: add --out <dir>. Same cause.
set LYNKEUS_REPORTS_URL and LYNKEUS_REPORTS_KEY (the organisation’s API key). One of the two variables is missing in this shell.
run: no cases.jsonl; pass the directory `case run --out` wrote. report push was given a directory that is not a run.
The run has no screenshots. The host takes none. The headless host renders nothing to capture; use a simulator or an emulator for runs that need pictures.
The run has no video. A case is filmed only where the host offers a record command; run npx lynkeus hello and look for record among its commands. The built-in headless host and a simulator or emulator session offer none, so a run there keeps its screenshots and no video. The case does not fail because of it.
Next steps
Section titled “Next steps”- Running on CI: push a report from every pull request.
- Flaky tests: read the history of a case.
- Backend traces: add the backend’s side to each step.
- Security: what the hosted service keeps.