Show Customers a Replay

View as Markdown

After a run finishes, give your users proof of what happened: a video-style replay they can scrub, next to a timeline of what the automation did. Both come from the run you already have - nothing extra to capture.

1import { Bctrl } from "@bctrl/sdk";
2
3const bctrl = new Bctrl({ apiKey: process.env.BCTRL_API_KEY! });
4
5// ... run finished, runtime stopped ...
6
7// Recordings are processed shortly after the run ends - poll until ready.
8let recording = await bctrl.runs.recording(runId, { expiresInSeconds: 86_400 });
9while (recording.status === "preparing" || recording.status === "processing") {
10 await new Promise((resolve) => setTimeout(resolve, 2_000));
11 recording = await bctrl.runs.recording(runId, { expiresInSeconds: 86_400 });
12}
13
14console.log(recording.url); // embed to replay
15console.log(recording.durationMs); // session length
16
17// The activity feed is the human-readable timeline of the same run.
18for await (const item of bctrl.runs.activity.iter(runId)) {
19 console.log(item.time, item.type, item.message);
20}

Embed the replay exactly like a live view:

1<iframe src="https://...recording url..." width="1280" height="800" style="border: 0"></iframe>

📸 Content TODO: screenshot of the finished layout - replay iframe on the left, activity timeline rendered as a list on the right. If you have the redesigned activity UI, a capture of a real run (navigation → typing → captcha solve → download) sells it best.

Rendering the timeline

Activity items are already aggregated for humans - a burst of keystrokes arrives as one evolving browser.type row, not five hundred key events. Each item has time, type, message, optional status, severity, and durationMs, and parent/child structure via children for grouped steps. Render message as the line, durationMs as the badge, and you have a customer-grade timeline without writing an aggregator.

If you need the raw record instead - every request, console line, and navigation - use events rather than activity.

Expiry

Like live view, the recording URL is a time-limited lease (expiresInSeconds, up to 24 hours). Mint a fresh one on each page load rather than storing it.

Next