API Quickstart

BetaWindow has a REST API for programmatic job creation, status polling, and webhook delivery. This is ideal for CI/CD pipelines where you want to trigger a human test after every deployment.

Authentication

All API routes require a Bearer token. Sign in via the web UI, then copy your token from Settings → API Token, or exchange credentials for a token:

# Exchange email/password for a JWT
curl -X POST https://sreaczlbclzysmntltdf.supabase.co/auth/v1/token?grant_type=password \
  -H "apikey: <SUPABASE_ANON_KEY>" \
  -H "Content-Type: application/json" \
  -d '{"email":"you@example.com","password":"your-password"}'

# Response:
# { "access_token": "eyJ...", "expires_in": 3600, ... }

Use access_token as your Bearer token in all subsequent requests.

Create a job

POST /api/jobs
Authorization: Bearer <token>
Content-Type: application/json

{
  "title": "Test signup + onboarding",
  "url": "https://your-app.vercel.app",
  "tier": "standard",
  "instructions": "1. Sign up with a new email\n2. Complete onboarding\n3. Create a project"
}

# Response 201:
{
  "job": {
    "id": "job_abc123",
    "status": "draft",
    "tier": "standard",
    "credits_cost": 10,
    "url": "https://your-app.vercel.app",
    "created_at": "2025-06-01T12:00:00Z"
  }
}

Tier values

tierCreditsDuration
quick510 min
standard1020 min
deep1530 min

Publish a job

Jobs start in draft status. Publish to make them visible to testers. Publishing holds the credit cost from your balance.

POST /api/jobs/{id}/transition
Authorization: Bearer <token>
Content-Type: application/json

{ "to": "published" }

# Response 200:
{
  "job": {
    "id": "job_abc123",
    "status": "published",
    "credits_held": 10
  }
}

Poll job status

GET /api/jobs/{id}
Authorization: Bearer <token>

# Response 200:
{
  "job": {
    "id": "job_abc123",
    "status": "completed",  // draft | published | in_progress | completed | cancelled | expired
    "credits_held": 0,
    "credits_spent": 10,
    "feedback_submitted_at": "2025-06-01T14:23:00Z"
  }
}

Job status lifecycle

draft → published → in_progress → completed
                ↓                  ↓
            cancelled          cancelled (rejected re-queue)
                ↓
            expired  (24h no claim)

Get feedback report

GET /api/jobs/{id}/feedback
Authorization: Bearer <token>

# Response 200:
{
  "feedback": {
    "verdict": "pass",
    "ux_score": 4,
    "summary": "Signup flow works. Minor issue with...",
    "bugs": [
      {
        "title": "Forgot password link 404s",
        "severity": "medium",
        "steps": "1. Go to /login\n2. Click Forgot password\n3. 404"
      }
    ],
    "network_log_url": "/api/sessions/{session_id}/network-log",
    "console_log_url": "/api/sessions/{session_id}/console-log"
  }
}

CI/CD integration example

Post a test job after every Vercel deployment in GitHub Actions:

# .github/workflows/e2e.yml
- name: Submit BetaWindow test job
  env:
    AGENTQA_TOKEN: ${{ secrets.AGENTQA_TOKEN }}
    DEPLOY_URL: ${{ steps.deploy.outputs.url }}
  run: |
    JOB=$(curl -sf -X POST https://betawindow.com/api/jobs \
      -H "Authorization: Bearer $AGENTQA_TOKEN" \
      -H "Content-Type: application/json" \
      -d "{"title":"CI deploy test","url":"$DEPLOY_URL","tier":"quick"}")
    JOB_ID=$(echo $JOB | jq -r '.job.id')
    
    # Publish the job
    curl -sf -X POST https://betawindow.com/api/jobs/$JOB_ID/transition \
      -H "Authorization: Bearer $AGENTQA_TOKEN" \
      -H "Content-Type: application/json" \
      -d '{"to":"published"}'
    
    echo "Job $JOB_ID published — results within 4 hours"

Rate limits

  • Job creation: 20 jobs per hour per account
  • Status polling: 60 requests per minute
  • Feedback retrieval: 30 requests per minute

Rate limit exceeded responses return HTTP 429 with a Retry-After header.

Errors

StatusMeaning
400Invalid request body (validation error)
401Missing or invalid Bearer token
402Insufficient credits to publish
403Forbidden (job belongs to another user)
404Job not found
422Invalid state transition (e.g. publish a completed job)
429Rate limit exceeded
500Internal server error

Code examples

Ready-to-run examples in Node.js and TypeScript: