Retest & remediation API
HTTP reference for retesting resolved findings, remediation SLAs and extra quality-gate rules that account for retest outcomes.
All paths are relative to https://api.aleex-rank.ai/api/v2 and authenticate with X-API-Key: rk_... (see REST API). A retest re-checks a resolved finding. It is not a scheduled re-run of the whole pentest, and it is not a manual reopen.
Retest one finding
POST /pentests/{id}/vulnerabilities/{vulnId}/retest
Optional auth context is used only for this run and is never returned on GET. cookies is a cookie header string, not an object. second_cookies is accepted the same way when an IDOR differential needs a second session:
{
"cookies": "session=abc; csrf=…",
"headers": {"Authorization": "Bearer eyJ..."}
}
The response is the run, including the queued finding rows. Ids are integers.
{
"success": true,
"data": {
"id": 77,
"pentest_id": 15,
"requested_by": 7,
"origin": "manual",
"status": "queued",
"scheduled_at": "2026-03-03 10:00:00",
"due_at": "2026-03-06 10:00:00",
"retest_outside_window": true,
"findings": [
{
"id": 501,
"run_id": 77,
"vulnerability_id": 42,
"result": "pending",
"run_status": "queued",
"pentest_id": 15
}
]
}
}
409 if a retest for that finding is already in flight:
{
"success": false,
"error": {
"message": "A retest is already pending for this finding",
"code": 409
}
}
Retest all eligible findings
POST /pentests/{id}/retest
Queues a run over every eligible resolved finding on the pentest (respecting cooldown and window policy). The same optional cookies / headers body is accepted.
{
"success": true,
"data": {
"id": 78,
"pentest_id": 15,
"origin": "manual",
"status": "queued",
"findings": [
{"id": 510, "run_id": 78, "vulnerability_id": 42, "result": "pending"},
{"id": 511, "run_id": 78, "vulnerability_id": 43, "result": "pending"}
]
}
}
List and inspect runs
GET /pentests/{id}/retests
GET /pentests/{id}/retests/{runId}
The list is a compact JSON array under data (not a paginated {items} envelope). Each row has origin, status, SLA timestamps, live summary tallies and finding_count. claimed_at, error, retest_outside_window and the findings array live only on the detail GET. auth_context is never echoed.
{
"success": true,
"data": [
{
"id": 77,
"pentest_id": 15,
"requested_by": 7,
"origin": "manual",
"status": "completed",
"scheduled_at": "2026-03-03 10:00:00",
"due_at": "2026-03-06 10:00:00",
"started_at": "2026-03-03 10:00:05",
"finished_at": "2026-03-03 10:04:12",
"retest_sla_breached_at": null,
"created_at": "2026-03-03 09:59:50",
"summary": {"pending": 0, "fixed": 1, "still_vulnerable": 0, "inconclusive": 0, "skipped": 0},
"finding_count": 1
}
]
}
The Python SDK wraps that array as .items. Detail:
{
"success": true,
"data": {
"id": 78,
"pentest_id": 15,
"origin": "manual",
"status": "completed",
"retest_outside_window": true,
"summary": {"pending": 0, "fixed": 1, "still_vulnerable": 1, "inconclusive": 1, "skipped": 1},
"findings": [
{"vulnerability_id": 42, "result": "fixed", "validation_method": "sqli-boolean/v1"},
{"vulnerability_id": 43, "result": "still_vulnerable", "validation_method": "xss-reflected/v1"},
{"vulnerability_id": 44, "result": "inconclusive", "skip_reason": "Target unreachable"},
{"vulnerability_id": 45, "result": "skipped", "skip_reason": "Class is not_validatable (OOB)"}
]
}
}
Results
| Result | Meaning |
|---|---|
pending | Queued or still running |
fixed | The issue did not reproduce |
still_vulnerable | The issue reproduced — the finding is reopened |
inconclusive | The run could not decide (target unreachable, missing cookies, budget) |
skipped | No validator (OOB / not_validatable), or the run was cancelled while queued |
A retest does not rewrite validation_status. still_vulnerable reopens the finding (open); the other results leave triage status alone.
Cancel
POST /pentests/{id}/retests/{runId}/cancel
Accepted only while the run is queued. A started run cannot be cancelled this way.
{
"success": true,
"data": {
"id": 78,
"status": "cancelled",
"findings": [
{"vulnerability_id": 42, "result": "skipped", "skip_reason": "Cancelled before the orchestrator claimed it"}
]
}
}
Remediation policy
GET /remediation-policy
PUT /remediation-policy
GET /teams/{id}/remediation-policy
PUT /teams/{id}/remediation-policy
Caller default and team override share the same object. GET returns both the effective policy and system_default. PUT returns the saved policy. System defaults:
| Field | Default | Notes |
|---|---|---|
sla_hours | {critical: 24, high: 168, medium: 720, low: 2160, info: null} | Hours to due date by severity |
retest_sla_hours | 72 | Target time to complete a retest |
auto_retest_on_resolve | false | Queue a retest when a finding is resolved |
auto_retest_on_ticket_close | true | Queue a retest when a linked integration ticket closes |
retest_cooldown_minutes | 15 | Minimum gap between retests of the same finding |
retest_outside_window | true | Allow retests outside the pentest RoE time windows |
quality_gate_rules | [] | Default rules merged into quality-gate evaluation |
{
"sla_hours": {"critical": 24, "high": 168, "medium": 720, "low": 2160, "info": null},
"retest_sla_hours": 72,
"auto_retest_on_resolve": false,
"auto_retest_on_ticket_close": true,
"retest_cooldown_minutes": 15,
"retest_outside_window": true,
"quality_gate_rules": [{"severity": "critical", "max_open": 0}]
}
{
"success": true,
"data": {
"policy": {
"id": 3,
"owner_type": "user",
"owner_id": 7,
"sla_hours": {"critical": 24, "high": 168, "medium": 720, "low": 2160, "info": null},
"retest_sla_hours": 72,
"auto_retest_on_resolve": false,
"auto_retest_on_ticket_close": true,
"retest_cooldown_minutes": 15,
"retest_outside_window": true,
"quality_gate_rules": [{"severity": "critical", "max_open": 0}]
},
"system_default": {
"owner_type": "system",
"sla_hours": {"critical": 24, "high": 168, "medium": 720, "low": 2160, "info": null},
"retest_sla_hours": 72
}
}
}
Quality gate extras
POST /pentests/{id}/vulnerabilities/quality-gate
GET /pentests/quality-gate
The per-pentest gate and the rolling gate on Pentests accept the same rule objects. In addition to severity + max_open, min_resolution_rate and max_overdue, these opt-in rules account for retests:
| Rule | Meaning |
|---|---|
min_verified_resolution_rate | Minimum share of resolved findings whose latest retest is fixed (0–1) |
max_unverified_resolved | Ceiling on resolved findings with no successful retest |
max_regressed | Ceiling on still_vulnerable / reopened outcomes |
max_pending_retests | Ceiling on retests still pending |
{
"rules": [
{"severity": "critical", "max_open": 0},
{"min_verified_resolution_rate": 0.9},
{"max_unverified_resolved": 0},
{"max_regressed": 0},
{"max_pending_retests": 2}
]
}