This is a personal proof-of-concept project. It is not intended for production use. Please refrain from using it.
Skip to content

The /analyze API

POST /api/v1/analyze is the embeddable engine's single entrypoint: give it an app URL and it autonomously generates a test suite, runs it in a real browser, and returns root-caused bugs — composing generate → run → root-cause so you don't orchestrate them yourself.

  • Auth: Authorization: Bearer tw_… (an org-scoped API token — see Authentication). Every call is isolated to that token's org.
  • Typed + documented: the request and response are Pydantic models, so the shape appears in the OpenAPI at GET /api/v1/openapi.json (and /api/v1/docs).

Request

jsonc
{
  "app_url": "https://staging.app.com",   // required (for kind=url)
  "kind": "url",                           // url | docs | text | openapi
  "base_url": null,                        // target app for non-url kinds
  "text": null,                            // pasted prose / spec (kind=text/openapi)
  "auth": {                                // optional: log in first (auth-gated apps)
    "username": "test@app.com",
    "password": "…",
    "login_path": "/login"
  },
  "exhaustive": true,                      // deeper coverage
  "file_issues": "gitlab",                 // github|gitlab|jira|native|off (default off)
  "mode": "sync",                          // sync = wait for the result; async = run_id only
  "timeout_s": 600,                        // sync wait budget (30–1800)

  // Per-request overrides (else the org's saved Settings are used):
  "anthropic_key": "sk-ant-…",             // BYOK for this call
  "log_backend": {                         // server-log source for root cause
    "LOG_BACKEND": "loki",
    "LOKI_URL": "https://loki.customer.internal:3100",
    "LOKI_SELECTOR": "{app=\"checkout\"}"
  }
}

Only app_url is required. mode: "async" returns immediately with a run_id you can poll via GET /api/v1/runs/{run_id}; mode: "sync" blocks up to timeout_s and returns the finished result.

Response — AnalyzeResult

jsonc
{
  "run_id": "run_1720…",
  "status": "done",                 // done | error | cancelled | queued | running
  "suite": "Checkout",
  "summary": { "passed": 5, "failed": 1, "broken": 0, "skipped": 0 },
  "cases": [
    {
      "id": "pay",
      "title": "Pay with card",
      "status": "failed",           // passed | failed | broken | skipped
      "why": "500 on submit",
      "root_cause": {
        "category": "backend_exception",
        "summary": "Checkout submit returned 500.  Server: NPE computing tax for DE",
        "suspected_cause": "NullPointerException computing tax for DE",
        "suggested_fix": "guard the null tax tier in PricingService",
        "confidence": 0.82,
        "server_status": "diagnosed",   // diagnosed | inferred_no_logs | no_trace_id | no_log_backend | no_logs_matched | diagnosis_failed
        "trace_id": "4bf92f35…",
        "evidence": ["[ERROR] NPE at PricingService.tax(...)"]
      }
    }
  ],
  "issues": [ /* the filed issue dicts, when file_issues was set */ ]
}

root_cause is null for passing cases. When there's no backend diagnosis, server_status says why (e.g. no_trace_id — the app didn't emit a trace header), so a gap is actionable rather than silent. See Connecting server logs.

Example

bash
# synchronous — one call, wait for root-caused bugs
curl -X POST https://YOUR-UPLIFTR/api/v1/analyze \
  -H "Authorization: Bearer tw_live_…" \
  -H "Content-Type: application/json" \
  -d '{
        "app_url": "https://staging.app.com",
        "file_issues": "gitlab",
        "log_backend": { "LOG_BACKEND": "loki", "LOKI_URL": "https://loki:3100" }
      }'

Prefer a client? Use the Python SDK. Calling from an agent? Use the MCP tool. See also the full REST API reference.

Upliftr · AI-native, self-healing E2E testing. Terms · Privacy · Legal Notice