Documentation

Set up AI-powered E2E tests for your deployed applications.

Overview

eze2e points an AI agent at your deployed URL and runs automated smoke tests. The agent controls a real browser (Chromium via Playwright), navigates your app, takes screenshots, and returns a pass/fail verdict with evidence.

How it works: You register an app (name + URL) in the dashboard. When you trigger a test — via the dashboard or API — eze2e spins up an ephemeral cloud machine, launches an AI agent with browser access, and lets it explore your app. Results are posted back within a few minutes.

App — A deployed website or web app you want to test. Each app has a base URL and its own API key.

Test Run — A single execution of the AI smoke test against a URL. Returns a decision (PASS/FAIL), summary, justification, and screenshots.

API Key — Authentication token for programmatic access. Two types: user keys (account management) and app keys (triggering tests).

Getting Started

1. Create an app

Sign in to the dashboard and click Add App. Give it a name and the production URL of your deployed application.

2. Run a test

From the app detail page, click Run Test. The AI agent will navigate your app, interact with it, and report back. Results appear in the dashboard within a few minutes.

3. Set up CI/CD

Generate an app API key from the app detail page (under CI / API). Use it to trigger tests from your deployment pipeline — see the below.

API Keys

User API Key

Account-level access. Use this to create apps, update settings, and manage your account programmatically. One user key at a time.

Format: eze2e_user_...

Generate in: Dashboard → API Keys

App API Key

Scoped to a single app. Use this in CI/CD to trigger test runs and poll for results. Cannot manage apps or access other apps. One key per app.

Format: eze2e_app_...

Generate in: App Detail → CI / API

All API requests use the Authorization: Bearer <key> header. Keys are shown once on creation and cannot be retrieved again.

Trigger a Test

POST /api/v1/test — Requires an app API key. The key identifies which app to test.

Request

curl -X POST https://www.eze2e.app/api/v1/test \
  -H "Authorization: Bearer eze2e_app_..." \
  -H "Content-Type: application/json" \
  -d '{"url": "https://preview-abc.vercel.app"}'

Body (optional JSON):

{
  "url": string  // Optional. Override the app's base URL.
                 // Useful for preview/staging deployments.
                 // Defaults to the app's registered URL if omitted.
}

Response

// 200 OK — test started
{
  "jobId": "a1b2c3d4-...",   // Use this to poll for results
  "status": "running"
}

// 400 Bad Request
{ "error": "Cannot test an archived app" }

// 402 Payment Required — quota exceeded
{
  "error": "quota_exceeded",
  "message": "Free plan limit of 5 runs/month reached.",
  "usage": {
    "runsUsed": 5,
    "runsIncluded": 5,
    "plan": "free"
  }
}

Poll Results

GET /api/v1/runs/:jobId — Requires an app API key. Returns the current status and, when complete, the full test result.

Request

curl https://www.eze2e.app/api/v1/runs/a1b2c3d4-... \
  -H "Authorization: Bearer eze2e_app_..."

Response

// While running
{
  "jobId": "a1b2c3d4-...",
  "status": "running",        // "pending" | "running"
  "decision": null,
  "summary": null,
  "justification": null,
  "targetUrl": "https://example.com",
  "startedAt": "2026-02-08T12:00:00.000Z",
  "completedAt": null,
  "screenshots": []
}

// When complete
{
  "jobId": "a1b2c3d4-...",
  "status": "completed",      // "completed" | "failed" | "skipped"
  "decision": "PASS",         // "PASS" | "FAIL" | "SKIPPED"
  "summary": "All smoke tests passed. The app loads correctly...",
  "justification": "Homepage renders with expected elements...",
  "targetUrl": "https://example.com",
  "startedAt": "2026-02-08T12:00:00.000Z",
  "completedAt": "2026-02-08T12:03:45.000Z",
  "screenshots": [
    {
      "type": "success",       // "success" | "failure"
      "url": "https://...",    // Screenshot image URL
      "context": "Homepage after initial load"
    }
  ]
}

Tip: Poll every 5-10 seconds. Tests typically complete in 2-5 minutes.

Manage Apps

These endpoints require a user API key (eze2e_user_...).

List apps

GET /api/v1/apps

curl https://www.eze2e.app/api/v1/apps \
  -H "Authorization: Bearer eze2e_user_..."

// Response
{
  "apps": [
    {
      "id": "cm...",
      "name": "My App",
      "url": "https://myapp.com",
      "createdAt": "2026-02-01T00:00:00.000Z"
    }
  ]
}

Create an app

POST /api/v1/apps

curl -X POST https://www.eze2e.app/api/v1/apps \
  -H "Authorization: Bearer eze2e_user_..." \
  -H "Content-Type: application/json" \
  -d '{"name": "My App", "url": "https://myapp.com"}'

// 201 Created
{
  "app": {
    "id": "cm...",
    "name": "My App",
    "url": "https://myapp.com",
    "createdAt": "2026-02-08T12:00:00.000Z"
  }
}

Update an app

PATCH /api/v1/apps/:id

curl -X PATCH https://www.eze2e.app/api/v1/apps/cm... \
  -H "Authorization: Bearer eze2e_user_..." \
  -H "Content-Type: application/json" \
  -d '{"url": "https://v2.myapp.com"}'

// 200 OK
{ "app": { "id": "cm...", "name": "My App", "url": "https://v2.myapp.com", ... } }

Archive an app

DELETE /api/v1/apps/:id — Soft-deletes the app. Frees up your app slot. Past test runs still count toward billing.

curl -X DELETE https://www.eze2e.app/api/v1/apps/cm... \
  -H "Authorization: Bearer eze2e_user_..."

// 200 OK
{ "success": true }

CI/CD Examples

Use your app API key to trigger tests after every deployment. Store the key as a repository secret.

GitHub Actions

Add EZE2E_API_KEY as a repository secret. This example handles quota errors gracefully so your pipeline continues even if you've run out of test runs.

# .github/workflows/e2e.yml
name: E2E Tests

on:
  deployment_status:

jobs:
  e2e:
    if: github.event.deployment_status.state == 'success'
    runs-on: ubuntu-latest
    steps:
      - name: Trigger eze2e test
        id: trigger
        run: |
          HTTP_CODE=$(curl -s -o /tmp/eze2e.json -w "%{http_code}" \
            -X POST https://www.eze2e.app/api/v1/test \
            -H "Authorization: Bearer ${{ secrets.EZE2E_API_KEY }}" \
            -H "Content-Type: application/json" \
            -d '{"url": "${{ github.event.deployment_status.target_url }}"}')

          if [ "$HTTP_CODE" = "402" ]; then
            echo "::warning::eze2e quota exceeded — skipping E2E test"
            echo "SKIP=true" >> $GITHUB_OUTPUT
            exit 0
          fi

          if [ "$HTTP_CODE" != "200" ]; then
            echo "::error::eze2e trigger failed (HTTP $HTTP_CODE)"
            cat /tmp/eze2e.json
            exit 1
          fi

          echo "JOB_ID=$(cat /tmp/eze2e.json | jq -r .jobId)" >> $GITHUB_OUTPUT

      - name: Wait for results
        if: steps.trigger.outputs.SKIP != 'true'
        run: |
          for i in $(seq 1 60); do
            RESULT=$(curl -s https://www.eze2e.app/api/v1/runs/${{ steps.trigger.outputs.JOB_ID }} \
              -H "Authorization: Bearer ${{ secrets.EZE2E_API_KEY }}")
            STATUS=$(echo $RESULT | jq -r .status)
            DECISION=$(echo $RESULT | jq -r .decision)

            case "$STATUS" in
              completed|failed|skipped)
                echo "Decision: $DECISION"
                echo "$RESULT" | jq .summary

                if [ "$DECISION" = "PASS" ]; then
                  exit 0
                elif [ "$DECISION" = "SKIPPED" ]; then
                  echo "::warning::eze2e test was skipped"
                  exit 0
                else
                  echo "::error::eze2e test failed — $DECISION"
                  exit 1
                fi
                ;;
            esac
            sleep 5
          done
          echo "::warning::eze2e timed out — test may still be running"
          exit 0

Shell script

#!/bin/bash
# Trigger and wait for eze2e test result
API_KEY="eze2e_app_..."

# Trigger (handle quota errors)
HTTP_CODE=$(curl -s -o /tmp/eze2e.json -w "%{http_code}" \
  -X POST https://www.eze2e.app/api/v1/test \
  -H "Authorization: Bearer $API_KEY")

if [ "$HTTP_CODE" = "402" ]; then
  echo "Quota exceeded — skipping E2E test"
  exit 0
fi

JOB_ID=$(cat /tmp/eze2e.json | jq -r .jobId)
echo "Test started: $JOB_ID"

# Poll until done
while true; do
  RESULT=$(curl -s https://www.eze2e.app/api/v1/runs/$JOB_ID \
    -H "Authorization: Bearer $API_KEY")
  STATUS=$(echo $RESULT | jq -r .status)

  case $STATUS in
    completed|failed|skipped) break ;;
    *) sleep 5 ;;
  esac
done

echo $RESULT | jq '{decision, summary, justification}'

Simple curl

# Trigger a test (uses app's registered URL)
curl -X POST https://www.eze2e.app/api/v1/test \
  -H "Authorization: Bearer eze2e_app_..."

# Trigger with URL override (e.g., preview deploy)
curl -X POST https://www.eze2e.app/api/v1/test \
  -H "Authorization: Bearer eze2e_app_..." \
  -H "Content-Type: application/json" \
  -d '{"url": "https://preview-abc.vercel.app"}'

# Check result
curl https://www.eze2e.app/api/v1/runs/<jobId> \
  -H "Authorization: Bearer eze2e_app_..."

Handling Results

Test runs end in one of three decisions. How you handle each in CI/CD determines whether a failed test blocks your deployment or just alerts your team.

Quota exceeded (HTTP 402)

When you hit your plan's run limit, the trigger endpoint returns a 402 instead of starting a test. Your CI pipeline should treat this as a warning, not a failure — you don't want a billing issue to block a production deployment.

# Check the HTTP status code before parsing the response
HTTP_CODE=$(curl -s -o response.json -w "%{http_code}" \
  -X POST https://www.eze2e.app/api/v1/test \
  -H "Authorization: Bearer $API_KEY")

if [ "$HTTP_CODE" = "402" ]; then
  echo "Warning: eze2e quota exceeded, skipping E2E test"
  exit 0   # Don't fail the pipeline
fi

To avoid hitting quota limits, upgrade your plan or enable overages in Dashboard → Billing.

PASS

The AI agent successfully navigated your app and found no issues. Your CI pipeline should continue normally.

FAIL

The AI detected a problem — a broken page, a crash, an unexpected error, or a critical flow that doesn't work. The summary and justification fields explain what went wrong, and screenshots provide visual evidence.

Whether you want a FAIL to block your deployment is up to you. In many cases, you'll want to alert your team instead of halting the pipeline. Forward the result to Slack, PagerDuty, or any webhook-capable service.

# Post failure details to Slack
if [ "$DECISION" = "FAIL" ]; then
  SUMMARY=$(echo $RESULT | jq -r .summary)
  RUN_URL="https://eze2e.app/dashboard/apps/$APP_ID"

  curl -X POST "$SLACK_WEBHOOK_URL" \
    -H "Content-Type: application/json" \
    -d "{
      \"text\": \"eze2e test failed\",
      \"blocks\": [
        {\"type\": \"section\", \"text\": {
          \"type\": \"mrkdwn\",
          \"text\": \"*eze2e test failed* :red_circle:\\n$SUMMARY\\n<$RUN_URL|View in dashboard>\"
        }}
      ]
    }"

  # Option A: Fail the pipeline
  exit 1

  # Option B: Warn but don't block
  # echo "::warning::E2E test failed — $SUMMARY"
  # exit 0
fi

Tip: Use exit code 1 to block the pipeline, or 0 with a warning annotation if you want to alert without blocking.

SKIPPED

The test was skipped, usually because the runner couldn't reach the URL, the app was archived, or there was an infrastructure issue. This is not a test failure — it means the test didn't run.

if [ "$DECISION" = "SKIPPED" ]; then
  echo "::warning::eze2e test was skipped — $SUMMARY"
  exit 0   # Don't fail the pipeline for a skip
fi

Recommended CI pattern

Most teams use this approach: PASS continues, FAIL alerts the team (Slack, email, etc.) and either blocks or warns depending on the environment, SKIPPED and quota issues warn but never block.

case "$DECISION" in
  PASS)    echo "E2E tests passed" ;;
  FAIL)    notify_slack "$SUMMARY"
           exit 1 ;;              # or exit 0 to warn only
  SKIPPED) echo "::warning::E2E test skipped"
           exit 0 ;;
esac

API Reference

Base URL: https://eze2e.app. All endpoints require an API key via the Authorization: Bearer header.

EndpointMethodAuthDescription
/api/v1/testPOSTApp keyTrigger a test run
/api/v1/runs/:jobIdGETApp keyGet test run status and results
/api/v1/appsGETUser keyList your apps
/api/v1/appsPOSTUser keyCreate an app
/api/v1/apps/:idGETUser keyGet app details
/api/v1/apps/:idPATCHUser keyUpdate app name or URL
/api/v1/apps/:idDELETEUser keyArchive an app

Error responses

All error responses follow the same shape:

{ "error": "string" }            // always present
{ "error": "string", "message": "..." }  // sometimes includes detail

Common status codes

  • 200 — Success
  • 201 — Created (new app)
  • 400 — Bad request (missing/invalid fields)
  • 401 — Missing or invalid API key
  • 402 — Quota exceeded
  • 403 — Wrong key type (e.g., app key on user endpoint) or app limit
  • 404 — Resource not found or not owned by you
  • 502 — Failed to spawn test runner

Coming Soon

BETA

Pentest Mode

AI-powered penetration testing. Automatically discover vulnerabilities, test auth flows for security issues, get detailed reports.

Custom Test Prompts

Tell the AI exactly what to test with natural language instructions. Describe actions, expected outcomes, and specific flows to verify.

Login Credentials

Provide test account credentials so the AI can test authenticated flows. Log in, navigate private pages, and verify protected features.

Webhook Notifications

Receive POST requests when test runs complete. Integrate with Slack, Discord, or your own systems.

GitHub Action

A first-party GitHub Action for one-line CI integration. Automatically test preview deployments on every PR.

Need help?

Email us at support@eze2e.app