Skip to content

API overview

Everything the Plainwright app does goes through a JSON REST API, so you can script it: create tests, trigger runs, wait for results, quarantine flaky tests or file issues. This page covers the basics. The REST API reference lists every endpoint with its parameters and response schema, generated from the backend’s OpenAPI schema.

With the default Docker Compose setup, the API is served through nginx under /api:

http://localhost:8080/api

On a deployed server use your public origin, such as https://plainwright.example.com/api. The examples on this page read it from PLAINWRIGHT_URL (without /api).

The API uses bearer tokens (JWT). Sign in once, keep the token, and send it on every request.

  1. Get a token from POST /api/auth/login. This one endpoint takes a form body (application/x-www-form-urlencoded) with username set to the account’s email, not JSON.
  2. Send it as Authorization: Bearer <access_token> on every other request.
  3. Get a new token when it expires (after 7 days by default; the server sets this with ACCESS_TOKEN_EXPIRE_MINUTES) or is revoked. Expired, revoked or invalid tokens get 401.
Terminal window
export PLAINWRIGHT_URL=http://localhost:8080
export PLAINWRIGHT_TOKEN=$(curl -fsS -X POST "$PLAINWRIGHT_URL/api/auth/login" \
--data-urlencode "username=ci-bot@acme.example" \
--data-urlencode "password=$PLAINWRIGHT_PASSWORD" | jq -r .access_token)
curl -fsS "$PLAINWRIGHT_URL/api/auth/me" -H "Authorization: Bearer $PLAINWRIGHT_TOKEN"
Login response
{
"access_token": "eyJhbGciOiJIUzI1NiIs…",
"token_type": "bearer",
"user": { "id": "…", "email": "ci-bot@acme.example", "name": "CI bot", "avatar_url": null, "email_verified": true, "onboarding_completed_at": null }
}

POST /api/auth/register (JSON {email, password, name}) creates an account and returns the same shape. The password must be 8 or more characters, at most 72 bytes, and not the email address. Google and GitHub sign-in are browser flows for the app: the callback hands the browser a one-time code, which the app trades for a token with POST /api/oauth/exchange (it only works in the browser that started the sign-in); see Sign-in and email.

Every token carries the account’s token version. These calls bump it, which revokes every token issued before:

Call Returns
POST /api/auth/change-password ({current_password, new_password}) {message, access_token}: a fresh token for the caller; every other session is signed out
POST /api/auth/logout-all {message, access_token}: the same, without changing the password
A completed password reset The account’s other sessions are signed out
Removal from the last project the account belonged to Every session of that account is signed out

Keep the access_token these calls return, or the next request with the old token gets 401.

Failed sign-ins, registrations and password-reset requests are rate-limited. A blocked request gets 429 with “Too many attempts — wait a few minutes and try again.” and a Retry-After header. Scripts that sign in once and reuse the token never hit these limits. The exact limits are in the security model.

Credential Header Works for
User token Authorization: Bearer <jwt> Every endpoint, limited by the user’s role on each project
Project webhook secret X-Webhook-Secret: <secret> Only POST /api/runs/webhook/{project_id} and GET /api/batches/{batch_id} (see CI webhook and gates)
None none Health (/health), login, register, password reset, OAuth providers, the Stripe webhook (signature-checked), and screenshot files

Each project gives a user one role: viewer, editor, admin, or owner (who created it). Endpoints check the role on the project the resource belongs to:

  • 404 means you can’t see the project at all, so Plainwright doesn’t reveal that it exists.
  • 403 with "Requires editor access" (or admin) means you can see it but your role is too low.

The full table of who can do what is in Team and roles.

Topic Rule
Bodies JSON (Content-Type: application/json), except login (form) and file uploads (multipart/form-data)
IDs UUID strings. A malformed ID returns 404 Not found, not 500. Batch IDs are 32 hex characters.
Errors {"detail": "message"}. Validation errors (422) carry FastAPI’s list of field errors in detail.
Times ISO 8601 strings in UTC. Some older fields use a space instead of T.
Status Runs have status (pending, running, passed, failed, error) and effective_status, which applies a human override. Use effective_status for decisions.
Paging GET /api/runs takes limit (up to 200) and offset and returns {total, limit, offset, runs}. GET /api/runs/test-case/{id} takes limit (1–100) and offset.
Queue down Endpoints that queue runs return 503 when Redis is unreachable. Retry later.
Secrets Values are never returned in full. Provider keys come back masked to their first 6 and last 4 characters; vault secrets and integration tokens as •••• plus at most the last 4; passwords fully masked.
Terminal window
H="Authorization: Bearer $PLAINWRIGHT_TOKEN"
# Projects you can access (each includes my_role)
curl -fsS "$PLAINWRIGHT_URL/api/projects/" -H "$H"
# Modules (board columns) and their tests
curl -fsS "$PLAINWRIGHT_URL/api/modules/project/$PROJECT_ID" -H "$H"
# Create a test (editor)
curl -fsS -X POST "$PLAINWRIGHT_URL/api/test-cases/" -H "$H" -H "Content-Type: application/json" \
-d '{"module_id": "'"$MODULE_ID"'", "name": "Guest checkout",
"natural_language": "Add any product to the cart, check out as a guest and pay with the test card. Check that an order number is shown.",
"kind": "ui", "retries": 1}'
# Run it against an environment in Firefox (editor)
RUN_ID=$(curl -fsS -X POST "$PLAINWRIGHT_URL/api/runs/trigger/$TEST_ID?environment_id=$ENV_ID&browser=firefox" \
-H "$H" | jq -r .id)
# Poll the run until it finishes
while :; do
STATUS=$(curl -fsS "$PLAINWRIGHT_URL/api/runs/$RUN_ID" -H "$H" | jq -r .effective_status)
case "$STATUS" in pending|running) sleep 5 ;; *) echo "$STATUS"; break ;; esac
done

POST /api/runs/trigger/{test_case_id} returns the new run straight away with status pending. Its optional query parameters are environment_id, browser (chromium, firefox, webkit) and device (desktop, iphone, pixel, ipad).

GET /api/runs/{run_id}/stream is a Server-Sent Events stream of the run’s live events: start, step, frame (screenshots), evidence, log, turn (conversation turns), eval_row, trial and finally done. It needs the bearer header, so use fetch or curl -N rather than the browser’s EventSource, which can’t send headers.

Terminal window
curl -N "$PLAINWRIGHT_URL/api/runs/$RUN_ID/stream" -H "Authorization: Bearer $PLAINWRIGHT_TOKEN"

Events are not stored: you see them from the moment you connect. Read the finished run with GET /api/runs/{run_id}.

The generated reference groups endpoints by these tags.

Area Prefix Covers
Auth /api/auth, /api/auth/password, /api/oauth Login, register, profile, password change and reset, sign out everywhere, email confirmation, Google/GitHub sign-in and the one-time code exchange
Projects /api/projects Projects, members, project files, webhook secret rotation, test notification
Modules /api/modules Board columns; run a module or the whole project
Test Cases /api/test-cases Create, read, update and delete tests
Templates, Onboarding /api/templates, /api/onboarding Starter tests and the guided setup
Runs /api/runs, /api/run-actions, /api/batches Trigger, list and stream runs; overrides, root cause, chat; batch status and CI gate
Reliability, Triage /api/reliability, /api/triage Flaky tests, quarantine, rerun failed, alert status; failure clusters and issue filing
Visual /api/visual Baselines and approvals
Evals /api/datasets Datasets, eval summaries and per-row results
Secrets /api/secrets, /api/auth-states The secret vault and saved logins
Integrations /api/integrations, /api/connectors Jira, Linear, GitHub, Figma, Notion, IMAP; drafting tests from them
AI Configs /api/ai-configs AI providers, connection tests, the supported model list (/models), Ollama models
Analytics /api/analytics Overview, trends, flakiness, usage, score trends
Environments /api/environments Per-project base URLs
AI Crawler /api/crawler Suggest tests from a URL, PDF or OpenAPI spec; follow and cancel a crawl
Billing, Files /api/billing, /api/files Plans and usage (when enabled); stored screenshots and evidence
System /api/system Configuration warnings for project owners and admins, such as a weak SECRET_KEY

/metrics (Prometheus) is internal and not part of the reference; see Monitoring.

The reference pages are generated from docs-site/openapi/plainwright.json. After changing backend endpoints, regenerate it from the docs site:

Terminal window
cd docs-site
npm run openapi

This imports the FastAPI app without starting it (no database, Redis or Docker needed) and writes the schema. Commit the updated JSON with your change; see Contributing.