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.
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 0Shell 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
fiTo 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.
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
fiTip: 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 ;;
esacAPI Reference
Base URL: https://eze2e.app. All endpoints require an API key via the Authorization: Bearer header.
| Endpoint | Method | Auth | Description |
|---|---|---|---|
| /api/v1/test | POST | App key | Trigger a test run |
| /api/v1/runs/:jobId | GET | App key | Get test run status and results |
| /api/v1/apps | GET | User key | List your apps |
| /api/v1/apps | POST | User key | Create an app |
| /api/v1/apps/:id | GET | User key | Get app details |
| /api/v1/apps/:id | PATCH | User key | Update app name or URL |
| /api/v1/apps/:id | DELETE | User key | Archive an app |
Error responses
All error responses follow the same shape:
{ "error": "string" } // always present
{ "error": "string", "message": "..." } // sometimes includes detailCommon status codes
200— Success201— Created (new app)400— Bad request (missing/invalid fields)401— Missing or invalid API key402— Quota exceeded403— Wrong key type (e.g., app key on user endpoint) or app limit404— Resource not found or not owned by you502— Failed to spawn test runner
Coming Soon
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