API Reference¶
All /api/* routes require authentication. Permission requirements are noted per endpoint.
Environments¶
| Method | Path | Permission | Description |
|---|---|---|---|
GET |
/api/environments |
view |
List all environments |
POST |
/api/environments |
manage |
Create an environment |
PATCH |
/api/environments/{envId} |
manage |
Update an environment |
DELETE |
/api/environments/{envId} |
manage |
Delete an environment |
Projects¶
| Method | Path | Permission | Description |
|---|---|---|---|
GET |
/api/environments/{envId}/projects |
view |
List projects in an environment |
POST |
/api/environments/{envId}/projects |
manage |
Create a project |
DELETE |
/api/environments/{envId}/projects/{projectId} |
manage |
Delete a project |
Upload - Strategy A: streaming¶
Upload a single zip file in one request.
Permission: upload
Headers:
Query parameters:
| Parameter | Required | Description |
|---|---|---|
buildId |
Yes | Unique build identifier |
Headers:
| Header | Required | Description |
|---|---|---|
Content-Type |
Yes | application/zip or application/octet-stream |
Body: raw zip bytes
Report generation is triggered automatically after the upload completes. The response is returned once the results are saved; generation runs server-side.
Upload - Strategy B: chunked¶
For large files (>50 MB). Initialise, send chunks, then complete.
1. Initialise¶
Permission: upload
Body (JSON):
{
"fileName": "results.zip",
"totalSize": 104857600,
"totalChunks": 3,
"buildId": "2024-01-15-001"
}
Response (201):
2. Upload a chunk¶
Permission: upload
Headers:
| Header | Description |
|---|---|
X-Chunk-Index |
Zero-based chunk index |
X-Total-Chunks |
Total number of chunks |
Body: raw chunk bytes
3. Complete¶
Permission: upload
Assembles all chunks, extracts the zip, and triggers report generation.
Reports¶
| Method | Path | Permission | Description |
|---|---|---|---|
POST |
/api/environments/{envId}/projects/{projectId}/reports |
upload |
Trigger report generation for a build |
GET |
/api/environments/{envId}/projects/{projectId}/reports |
view |
List reports |
GET |
/api/environments/{envId}/projects/{projectId}/reports/stats |
view |
Aggregated pass/fail stats |
DELETE |
/api/environments/{envId}/projects/{projectId}/reports/{buildId} |
manage |
Delete a report and its files |
Trigger report generation¶
Permission: upload
Body (JSON):
{
"buildId": "2024-01-15-001",
"reportConfig": {
"name": "My Report",
"qualityGate": {
"rules": [
{ "maxFailures": 2, "fastFail": true }
]
},
"plugins": {
"allure2": {
"options": {
"reportName": "My Report",
"singleFile": false,
"reportLanguage": "en"
}
},
"dashboard": {
"options": {
"reportName": "My Dashboard",
"singleFile": false,
"reportLanguage": "en"
}
},
"csv": {
"options": {
"fileName": "allure-report.csv"
}
}
},
"variables": {
"env": "production"
}
}
}
buildId must match the one used during upload. reportConfig is optional - if omitted, the server's default allurerc.yml is used.
The reportConfig body maps directly to the allurerc.yml schema. The server merges your overrides on top of its base config. Two keys are always server-controlled and cannot be overridden: output (report output path) and historyPath (trend chart history).
Response (202):
Sending allurerc.yml from CI¶
If you manage report config as an allurerc.yml file in your repo, parse and inline it into the reportConfig field when calling this endpoint:
Upload sessions¶
| Method | Path | Permission | Description |
|---|---|---|---|
GET |
/api/uploads |
view |
List all upload sessions (all envs/projects) |
GET |
/api/uploads/stream |
view |
SSE stream of live upload events |
DELETE |
/api/uploads/{id} |
manage |
Delete a session record and associated files |
SSE stream¶
GET /api/uploads/stream returns a persistent text/event-stream connection. Each event is a JSON-encoded UploadSession object:
Phases: uploading → assembling → generating → done / failed
Notifications¶
Notification APIs are provided by go-notify and mounted at /api/notifications/*.
| Method | Path | Permission | Description |
|---|---|---|---|
GET |
/api/notifications |
view |
List notifications (newest first) |
GET |
/api/notifications/stream |
view |
SSE stream (event: notification) |
GET |
/api/notifications/unread |
view |
Unread count |
PATCH |
/api/notifications/{id}/read |
view |
Mark one notification as read |
POST |
/api/notifications/read |
view |
Mark all notifications as read |
DELETE |
/api/notifications/{id} |
view |
Delete one notification |
List notifications¶
Query parameters:
| Parameter | Required | Description |
|---|---|---|
unread |
No | true to return unread-only notifications |
limit |
No | Max number of notifications to return (0 means no cap) |
Response (200):
[
{
"id": "0195f3a2-...",
"title": "Report ready",
"body": "Your export has finished.",
"category": "upload",
"severity": "success",
"read": false,
"created_at": "2026-04-04T10:30:00Z",
"payload": { "url": "/reports/build1/index.html" }
}
]
Unread count¶
Response (200):
Read-state endpoints¶
PATCH /{id}/read returns 404 when the notification ID does not exist.
Streaming¶
SSE endpoint: GET /api/notifications/stream
SSE payload format:
Overview¶
| Method | Path | Permission | Description |
|---|---|---|---|
GET |
/api/overview |
view |
Dashboard analytics summary, trends, top failing projects, and recent builds |
Response (200):
{
"summary": {
"totalEnvironments": 3,
"totalProjects": 18,
"totalBuilds": 1240,
"totalPassed": 98210,
"totalFailed": 4170,
"overallPassRate": 95
},
"dailyTrends": [
{ "date": "2026-04-01", "passed": 1200, "failed": 42, "skipped": 15, "buildCount": 14 }
],
"topFailingProjects": [
{
"envId": "staging",
"projectId": "checkout",
"projectName": "Checkout Service",
"envName": "Staging",
"totalFailed": 310,
"totalBuilds": 80,
"passRate": 88
}
],
"recentBuilds": []
}
Health¶
| Method | Path | Auth | Description |
|---|---|---|---|
GET |
/api/healthz |
None | Liveness probe - returns 200 OK |
GET |
/api/version |
None | Build version, time, and Go version |
/api/version response¶
Settings¶
Settings endpoints require an active session (OAuth login). API keys cannot be used to manage settings.
API Keys¶
| Method | Path | Permission | Description |
|---|---|---|---|
GET |
/api/settings/apikeys |
manage |
List all API keys (hashes never returned) |
POST |
/api/settings/apikeys |
manage |
Create a new API key |
DELETE |
/api/settings/apikeys/{id} |
manage |
Revoke or permanently delete a key |
Create API key¶
Body (JSON):
role must be one of admin, developer, or viewer.
Response (201):
Save the key now
The plaintext key is returned once at creation time. The server stores only its SHA-256 hash and cannot recover the original value.
Delete or revoke API key¶
- Default (
actionomitted oraction=revoke): soft-delete - key is marked inactive but the record is retained. action=delete: permanently removes the key record.
Users¶
| Method | Path | Permission | Description |
|---|---|---|---|
GET |
/api/settings/users |
manage |
List all OAuth users who have signed in |
PATCH |
/api/settings/users/{email}/role |
manage (admin only) |
Override a user's role |
DELETE |
/api/settings/users/{email}/role |
manage (admin only) |
Reset a user's role to the YAML baseline |
Returns users ordered by most recent login. Each entry includes email, name, avatarUrl, provider, role, firstLoginAt, and lastLoginAt.
Role changes take effect on the user's next login.
Disk Usage¶
| Method | Path | Permission | Description |
|---|---|---|---|
GET |
/api/settings/disk |
manage |
Storage consumed by the data directory |
GET |
/api/settings/disk/notification-threshold |
manage |
Get disk-usage alert threshold percentage |
PUT |
/api/settings/disk/notification-threshold |
manage |
Update disk-usage alert threshold percentage |
Results are cached for 60 seconds on the server to keep the endpoint fast.
Response (200):
{
"usedBytes": 2147483648,
"freeBytes": 53687091200,
"totalBytes": 107374182400,
"breakdown": [
{ "path": "production/checkout", "bytes": 1073741824 },
{ "path": "staging/checkout", "bytes": 536870912 }
]
}
| Field | Description |
|---|---|
usedBytes |
Total bytes consumed by all files under DATA_DIR |
freeBytes |
Available bytes on the filesystem (Bavail × Bsize). 0 if the stat call fails. |
totalBytes |
Total filesystem capacity. 0 if the stat call fails. |
breakdown |
Up to 20 env/project pairs sorted by size descending |
Disk notification threshold¶
Response (200):
85 is the default when no value has been saved yet.
Body (JSON):
| Status | Reason |
|---|---|
204 |
Updated successfully |
400 |
Invalid body or thresholdPercent outside 0..100 |
500 |
Failed to persist the setting |
Allure CLI¶
| Method | Path | Permission | Description |
|---|---|---|---|
GET |
/api/settings/allure |
manage |
Get installed version and latest npm release |
PUT |
/api/settings/allure |
manage (admin only) |
Install a specific Allure CLI version |
Get Allure version¶
Response (200):
| Field | Description |
|---|---|
version |
The Allure CLI version currently installed on the server (allure --version) |
latest |
The latest version published to npm (registry.npmjs.org/allure/latest), cached for 1 hour. Empty string if the registry is unreachable. |
The UI shows an Update available banner in Settings → Allure CLI when latest differs from version, and an amber dot on the Settings nav link.
Install a version¶
Permission: manage session + admin role
Runs npm install -g allure@<version> on the server. The updated binary is used immediately for all subsequent report generations - no restart required.
Body (JSON):
version must be a valid semver string (MAJOR.MINOR.PATCH). Pre-release tags (e.g. 3.4.0-beta.1) are not accepted. This endpoint works for both upgrades and downgrades.
Response (200):
Error responses:
| Status | Reason |
|---|---|
400 |
Missing or invalid semver version string |
403 |
Caller is not an admin |
500 |
npm install failed (stderr included in response body) |
Data Retention¶
| Method | Path | Permission | Description |
|---|---|---|---|
GET |
/api/settings/retention |
manage |
Get current retention settings |
PUT |
/api/settings/retention |
manage |
Update retention settings |
GET |
/api/settings/retention/runs |
manage |
List recent cleanup sweep runs |
Get retention settings¶
Response (200):
Update retention settings¶
Body (JSON):
| Field | Description |
|---|---|
retentionDays |
Reports older than this many days are permanently deleted. Minimum: 1. |
intervalHours |
How often the cleanup worker runs, in hours. Minimum: 1. |
dryRun |
If true, the worker logs what it would delete but takes no action. |
Response: 204 No Content
List cleanup runs¶
Returns the most recent cleanup sweep records, ordered newest first. At most 5 records are retained in the database; older runs are automatically pruned after each sweep.
Query parameters:
| Parameter | Default | Description |
|---|---|---|
limit |
5 |
Number of records to return (max 50) |
Response (200):
[
{
"id": "01HXY...",
"startedAt": "2024-01-15T02:00:00Z",
"finishedAt": "2024-01-15T02:00:03Z",
"status": "success",
"deletedCount": 12,
"skippedCount": 0,
"dryRun": false
},
{
"id": "01HXX...",
"startedAt": "2024-01-14T20:00:00Z",
"finishedAt": "2024-01-14T20:00:00Z",
"status": "failed",
"deletedCount": 0,
"skippedCount": 0,
"dryRun": false,
"errorMessage": "cleanup: list expired builds: context deadline exceeded"
}
]
| Field | Description |
|---|---|
status |
success or failed |
deletedCount |
Number of builds removed (or would-be removed in dry-run mode) |
skippedCount |
Builds that encountered a per-record error and were skipped |
dryRun |
Whether this run was a dry-run (no data was actually deleted) |
errorMessage |
Present only on failed runs - the top-level error that aborted the sweep |
Auth endpoints¶
| Method | Path | Description |
|---|---|---|
GET |
/auth/{provider} |
Start OAuth flow (provider = google) |
GET |
/auth/{provider}/callback |
OAuth callback |
POST |
/auth/logout |
Clear session |
GET |
/auth/me |
Current user or 401 |
Error responses¶
All error responses use JSON:
| Status | Meaning |
|---|---|
400 |
Invalid request body or parameters |
401 |
Not authenticated |
403 |
Authenticated but missing permission |
404 |
Resource not found |
409 |
Conflict (e.g. duplicate build ID) |
500 |
Internal server error |