Skip to content

Service HTTP API

The service listens on port 4400 (configurable via SERVICE_PORT). The browser package and fixer talk to it automatically; these endpoints are also yours to script against — the dashboard is built entirely on the read endpoints.

Script registration

POST /api/scripts/register

Called by the browser package on chromium.launch(). Registration is best-effort — scripts run fine if it fails.

{
"scriptId": "portal-scraper",
"repoUrl": "https://github.com/your-org/scrapers",
"entryFile": "src/scraper.ts",
"branch": "main",
"maxFailures": 3
}

Returns 201 with { scriptId, state, registeredAt }. scriptId is the only required field (400 without it).

GET /api/scripts/:id/status

Current state of one script: { scriptId, state, failureCount, maxFailures, lastError, pendingErrors, registeredAt, repoUrl, entryFile, branch }. state is 'active' or 'fixing' — there is no blocked state.

POST /api/scripts/:id/unblock

Resets a script to { state: 'active', failureCount: 0 }. Kept for operational hygiene; since Autowright never blocks, you rarely need it.

Error intake

POST /api/errors

Called by the browser package on failure. Body is a full ErrorContext (scriptId and hash required).

{ "accepted": true, "deduplicated": false, "scriptId": "portal-scraper" }

Every error is accepted (200). If a fix for the same hash was created within the dedupe window (AUTOWRIGHT_DEDUPE_TTL_MS, default 24h), deduplicated is true and no new fix job is queued — the incident is already being handled. Unknown scriptId returns 404.

Fix queue (used by the fixer)

GET /api/errors?status=pending

How the fixer polls for work. Returns queued items with their errorContext and the script’s registration (repo, branch, entry file). Valid statuses: pending, in-progress, completed, failed.

GET /api/errors/:scriptId

All error metadata for one script — classification, message, step, artifact manifest per item.

GET /api/errors/:scriptId/:fixRequestId

One error in full: timestamps, complete errorContext (including the artifact bundleUrl), and the result if the fix has finished.

PATCH /api/errors/:scriptId/:fixRequestId

How the fixer reports progress:

{ "status": "completed", "result": { "success": true, "fixType": "pr", "prUrl": "…", "summary": "…", "filesChanged": ["src/scraper.ts"], "cost": { "usd": 0.09, "turns": 6 } } }

status must be in-progress, completed, or failed. Side effects on the script record: in-progress sets state: 'fixing'; completed resets the failure counter and returns the script to active; failed also returns it to active — a failed fix never blocks the script.

Dashboard reads

GET /api/stats

Headline aggregates:

{
"totalRuns": 42,
"running": 1,
"fixed": 37,
"notFixed": 4,
"totalCostUsd": 4.83,
"avgCostUsd": 0.13,
"totalTurns": 268
}

running counts pending + in-progress; fixed is completed; notFixed is failed (surfaced as “needs attention”).

GET /api/fixes

Global list of all fixes, with the fields a table needs:

{
"total": 42,
"fixes": [{
"id": "fix-a1b2c3d4-1781067588938",
"scriptId": "portal-scraper",
"status": "completed",
"classification": "SELECTOR_NOT_FOUND",
"errorMessage": "Timeout 30000ms exceeded…",
"step": { "name": "click", "action": "click", "selector": "#login-btn", "url": "…" },
"createdAt": "…", "startedAt": "…", "completedAt": "…",
"prUrl": "https://github.com/your-org/scrapers/pull/17",
"filesChanged": ["src/scraper.ts"],
"costUsd": 0.09,
"turns": 6
}]
}

GET /api/fixes/:id

The full FixQueueItem for one fix — complete error context, result, cost breakdown, and artifact manifest. 404 if the id is unknown.