Skip to main content
Every error is JSON with a human-readable error string. Some carry more:
error is written for a person and can change. Branch on the status and on code when there is one.

By status

Both 402 codes carry an upgradeUrl pointing at the pricing page. Neither 429 quota code carries Retry-After, because the wait is not a fixed number of seconds.

Runs that are accepted and then do not run

POST /api/runs/trigger answers 202 once the run is created. A few things can still stop it after that, and they are recorded on the run rather than returned by the trigger:
  • Every test browser is busy. The run waits in line. If no browser frees up within 3 minutes of reaching the front, or 10 minutes in line altogether, it ends with status: "cancelled" and the reason in reportMarkdown.
  • The plan’s cap was reached by another run in the meantime. The run ends cancelled with the reason.
  • Setup failed before the browser started. The run ends cancelled with a neutral reason.
A cancelled run has no verdict: overallStatus is null. Poll GET /api/runs/{id} until status is terminal, then read overallStatus.

The 503 code

service_busy (503) is the service’s own capacity, never your quota. The trigger endpoint does not return it: a triggered run waits in line instead, as above.

Retrying safely

  • Retry 429 and 500, with backoff.
  • Do not retry 400, 401, 402, 403 or 404: the same request gets the same answer.
  • A retried trigger is a second run. The API has no idempotency key, so after a trigger that timed out without an answer, check GET /api/projects/{projectId}/runs before sending it again.