Skip to main content
Browser jobs should leave enough evidence to answer three questions:
  1. What did the browser do?
  2. What did the page show?
  3. Why did the job stop, retry, or ask for review?
Webcompute gives you browser status, Playwright execution logs, observations, events, recordings, downloads, generated files, screenshots, and artifact IDs. Your application should connect those signals to its job ID, user request, policy, retry attempts, and final result.

What to store

SignalStore
Job metadataYour job ID, target URL, policy summary, timeout, retry count, and user-visible status
Browser identityBrowser ID and lifecycle status
Execution evidenceStep result, page URL/title, bounded observation summary, and redacted logs
ArtifactsScreenshot IDs, recording segment IDs, generated file IDs, download IDs, and replay URLs when returned
ErrorsRedacted code, name, and message; avoid raw request bodies and signed URLs
Review stateBlocker, CAPTCHA, approval, or policy signal that caused the job to stop

SDK job log shape

import { redactValueForObservability } from "@webcompute/sdk";

function safeError(error: unknown) {
  if (error && typeof error === "object") {
    const value = error as { code?: unknown; name?: unknown; message?: unknown };
    return redactValueForObservability({
      code: value.code,
      name: value.name,
      message: value.message,
    });
  }
  return { message: redactValueForObservability(String(error)) };
}

const result = await browser.playwright.execute({
  code: `
    await page.goto("https://example.com");
    return { title: await page.title(), url: page.url() };
  `,
  capture: { status: true, observation: { kind: "aria", includeOn: "always" } },
});

await logBrowserStep({
  jobId,
  browserId: browser.id,
  success: result.success,
  page: result.page,
  status: result.status,
  observation: result.observation
    ? {
        kind: result.observation.kind,
        chars: result.observation.chars,
        truncated: result.observation.truncated,
      }
    : undefined,
  artifacts: result.artifacts,
  error: result.error ? safeError(result.error) : undefined,
});
Do not store full observations by default. Store summaries and artifact IDs, then fetch deeper evidence only when an operator needs it.

Events and recordings

Use events for timelines and recordings for visual review. Recording is opt-in at browser creation.
const browser = await web.browser.create({ recording: true });

const events = await browser.events({ limit: 50 });
const recording = await browser.recordings.get();
const segments = await browser.recordings.segments({ limit: 10 });
Keep signed Debug UI and CDP URLs out of shared logs. They are bearer capabilities.

Trace IDs

The public docs should not invent a trace ID contract. If your deployment exposes request IDs or trace IDs, store them with your app job record. Otherwise, correlate by your job ID, browser ID, timestamps, event IDs, and artifact IDs. Reference: observations, recordings, status, events, and logs, SDK resources reference, and errors and retries.