REST API
Upliftr's backend is a FastAPI JSON API. Everything the dashboard does, you can do over HTTP, author suites, start and observe runs, manage issues and settings, generate suites from a URL, export to Playwright, and read analytics.
- Base path:
/api/v1 - Auth: every endpoint requires
Authorization: Bearer <jwt-or-api-token>, except the open ones:GET /health,GET /health/ready,POST /auth/login, and the first-run bootstrapGET /auth/setup+POST /auth/setup(the latter only works while no users exist). See Authentication. - Interactive OpenAPI / Swagger UI:
/docs, try every endpoint live, with full request / response schemas. - Raw schema:
/openapi.json
The live OpenAPI page is the source of truth for exact request and response shapes, the tables below are a map, not a substitute. Generate clients from
/openapi.json.
Endpoints
Health
| Method | Path | Description |
|---|---|---|
GET | /health | Liveness, always 200 if serving. |
GET | /health/ready | Readiness, 200 if the data dir is writable, else 503. |
Auth
Full details in Authentication.
| Method | Path | Description |
|---|---|---|
GET | /auth/setup | Open. { needs_setup }, true while the instance has no admin yet. |
POST | /auth/setup | Open (only valid pre-first-user). { email, password } creates the first admin → { token, user }. |
POST | /auth/login | { email, password } → { token, user }. Open (no auth). |
GET | /auth/me | The current user. |
POST | /auth/tokens | Create an API token { name? } → returns the raw tw_… once. |
GET | /auth/tokens | List your API tokens (metadata only). |
DELETE | /auth/tokens/{id} | Revoke an API token. |
GET | /auth/users | List users (admin). |
POST | /auth/users | Create a user { email, password, role } (admin). |
Suites
| Method | Path | Description |
|---|---|---|
GET | /suites | List suites (paginated: limit, offset, q). |
POST | /suites | Create a new suite, body { yaml }. The server picks a unique filename (never overwrites). Returns { ok, file }. |
GET | /suites/{name} | Get a suite ({ file, yaml, cases }). |
PUT | /suites/{name} | Update a suite, body { yaml }; validates YAML, returns 400 on error. |
DELETE | /suites/{name} | Delete a suite. |
GET | /suites/{name}/export | Export the suite as a portable Playwright .spec.ts (text/plain). |
Runs
Runs are persisted to a durable queue and worked by a pool of up to UPLIFTR_MAX_CONCURRENT_RUNS (default 2) workers, submit any number; they run in parallel up to that limit and the rest wait their turn (no 409). The queue survives a restart; a run interrupted by a crash is reconciled on the next startup. Each job is its own run id. State within a suite (generated fixtures and values a case capture:s) is shared across that suite's cases.
| Method | Path | Description |
|---|---|---|
GET | /runs | Run history (includes queued / running jobs). |
POST | /runs | Enqueue one or more jobs. Single-suite form: { suite, cases?, file_issues?, sharded? }. Queue form: { jobs: [{ suite, cases? }, …], file_issues? }. cases is a subset of case ids (omit for all). Runs are headless. Returns { run_id, run_ids, queued }. |
GET | /runs/queue | Currently running + pending jobs, { active, active_suite, running: [{ id, suite }], running_suites, pending: [{ id, suite, cases }], busy } (active/active_suite keep the single-run shape for back-compat). |
GET | /runs/{id} | Run status + live events + report. Events include action (each LLM browser action) and shot (a per-step screenshot path). |
Run artifacts (per-step screenshots, reports) are served read-only under /api/v1/artifacts/<run_id>/…; each case in the report carries a shots[] list of { label, note, path } you can render from that base.
Plans (ordered regression passes)
A plan bundles suites into an ordered pass you run on demand or on a cron schedule.
| Method | Path | Description |
|---|---|---|
GET | /plans | List plans. |
GET | /plans/{plan_id} | Plan detail (suites, schedule, last run). |
POST | /plans | Create/update a plan, body { id?, name, description?, items: [{ suite, cases? }], schedule?, auth? } (schedule is a cron expression; set id to update). |
DELETE | /plans/{plan_id} | Delete a plan. |
POST | /plans/{plan_id}/run | Enqueue the whole plan as one ordered pass → { run_ids }. |
Issues (built-in tracker)
| Method | Path | Description |
|---|---|---|
GET | /issues?status= | List native issues (paginated; optional status, q). |
GET | /issues/{id} | Issue detail ({ …, body_md, comments }). |
PATCH | /issues/{id} | Partial edit, any of { status, title, body_md, severity }. status is open / closed / acknowledged. |
POST | /issues/{id}/comments | Add a comment, body { body }. |
Settings (admin)
Writing settings requires an admin.
| Method | Path | Description |
|---|---|---|
GET | /settings | Integration settings, secrets are masked on read. |
PUT | /settings | Save settings, body is the settings object (only non-null fields are written). |
POST | /settings/test-notification | Send a test Slack/Teams notification with the current config. |
Integrations
| Method | Path | Description |
|---|---|---|
GET | /integrations/trackers | Which issue trackers are configured: { github, gitlab, jira } booleans. Drives the dashboard's tracker picker so it only offers what's set up. |
Artifacts
| Method | Path | Description |
|---|---|---|
GET | /artifacts/{path} | A run artifact (per-step screenshot, report). Authenticated, send the Bearer token, or ?token=<jwt> for <img>/<a> tags. Confined to the artifacts directory. |
Generate (anything → suite)
Generate a suite from a live app, your docs/spec, or a description. Exhaustive by default, a multi-pass generator (discover areas → expand each into happy-path, validation, edge, error, auth and negative scenarios in parallel).
| Method | Path | Description |
|---|---|---|
POST | /generate | Body { kind, url?, base_url?, text?, auth?, exhaustive?, save_as? }. kind is url | docs | text | openapi. For url, pass url (a deep page works; add auth: { username, password, login_path? } to reach pages behind login). For docs/text/openapi, pass base_url (what the suite targets) plus a url to fetch or pasted text. Returns { yaml } (plus saved_as). See generation. |
POST | /generate/plan | Exhaustive generation that splits the app into several suites and saves them as a ready-to-run plan. Returns { plan_id, plan_name, suites }. |
Analyze (embeddable engine — one call → root-caused bugs)
The single entrypoint for embedding Upliftr: generate + run + root-cause in one call. See Embedding Upliftr and the /analyze contract.
| Method | Path | Description |
|---|---|---|
POST | /analyze | Body { app_url, kind?, auth?, exhaustive?, file_issues?, mode?, timeout_s?, anthropic_key?, log_backend? }. Generates a suite for app_url, runs it, and returns a typed AnalyzeResult { run_id, status, summary, cases[{status, why, root_cause}], issues }. mode: "sync" (default) waits; "async" returns a run_id to poll via /runs/{id}. anthropic_key / log_backend override the org's saved settings per request. |
Tracing (root-cause helper)
| Method | Path | Description |
|---|---|---|
GET | /tracing/snippet.js | Open (no auth). The drop-in browser trace-propagation snippet — a dependency-free script that makes an app's same-origin fetch/XHR emit a W3C traceparent, so a UI failure resolves to the backend log line. See Connecting server logs. |
Analytics
| Method | Path | Description |
|---|---|---|
GET | /analytics | Flake / run analytics over the full run history. See Flakiness analytics. |
Checks (standalone)
Dry-run a single deterministic check core without authoring a whole suite. These are not suite expect kinds (suites use the four check kinds), they are standalone endpoints for dashboards and visual baselines.
| Method | Path | Description |
|---|---|---|
POST | /checks/api | Body { url, method?, status?, json_path?, equals?, headers?, body? }. Runs an HTTP request and asserts status and/or a JSON path. Returns { kind: "api", status, observed }. |
POST | /checks/screenshot | Body { name, image_b64, tol? }. Compares a PNG against the stored baseline for name (first run for a name seeds the baseline and passes). Returns { kind: "screenshot_region", status, observed }. |
Examples
Start a run:
curl -X POST http://127.0.0.1:8400/api/v1/runs \
-H 'Content-Type: application/json' \
-d '{"suite": "signup-login.upliftr.yaml", "file_issues": "upliftr"}'Poll its status, live events, and report:
curl http://127.0.0.1:8400/api/v1/runs/<run-id>Save (and validate) a suite, the body is JSON { "yaml": "..." }:
curl -X PUT http://127.0.0.1:8400/api/v1/suites/signup-login.upliftr.yaml \
-H 'Content-Type: application/json' \
-d "{\"yaml\": $(jq -Rs . < signup-login.upliftr.yaml)}"Generate a suite from a URL and save it:
curl -X POST http://127.0.0.1:8400/api/v1/generate \
-H 'Content-Type: application/json' \
-d '{"url": "https://app.example.com", "save_as": "app-flows"}'Export a suite to portable Playwright:
curl http://127.0.0.1:8400/api/v1/suites/signup-login.upliftr.yaml/export -o tests/signup.spec.tsList open issues from the built-in tracker:
curl 'http://127.0.0.1:8400/api/v1/issues?status=open'Error shape
Errors are returned as JSON via the app's error handlers, e.g. a missing/invalid token returns 401; an action needing admin returns 403; invalid YAML on PUT /suites/{name} returns 400 with a type of invalid_yaml; generation failures return 502 (generation_error).
See also
- Writing tests, the YAML you
PUTto/suites. - Runs & assertions, what
/runs/{id}reports. - Filing issues, the
/issuesand/settingssurface. - CLI · Settings & env.