A scan suite is a saved selection of probes and endpoints. Suites are created and edited in the console at /app/suites; the API exposes them so automation can start a scan by naming a suite instead of carrying its probe list.
| Route | Scope |
|---|---|
GET /api/v2/suites | suites:read |
POST /api/v2/scans with suite_id | scans:write |
Use a suite when the probe selection is a policy decision that belongs with the security team rather than with the caller. A pipeline that passes probe_ids pins a list that goes stale as probes are added, renamed or retired; a pipeline that passes suite_id picks up the current selection on the next run.
List suites
curl -sS https://penaxtra.com/api/v2/suites \
-H "Authorization: Bearer $TOKEN"
{
"data": [
{
"id": "2a1e2496-8d50-40db-af5e-99e5eb7ee8bb",
"name": "EU AI Act Evidence Pack - Monthly",
"description": "Probe set the compliance team signs off on",
"is_active": true,
"schedule_cron": "0 3 1 * *",
"last_run_at": "2026-08-01 03:00:11+00",
"created_at": "2026-06-14 10:22:03+00",
"endpoint_count": 5,
"probe_count": 44
}
]
}
Returns the 200 most recent suites for the tenant, newest first. probe_ids and endpoint_ids are summarised as counts rather than returned in full; probe content is not exposed over the API.
is_active reflects whether the suite's schedule is enabled. It does not prevent a suite from being used in a manual scan, so check it before treating last_run_at as evidence of a running schedule.
Start a scan from a suite
curl -sS -X POST https://penaxtra.com/api/v2/scans \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"endpoint_id": "11a3dd55-5369-4020-8d11-2652c26fe0d1",
"suite_id": "2a1e2496-8d50-40db-af5e-99e5eb7ee8bb"
}'
endpoint_id is still required and still names the single endpoint under test. suite_id supplies the probe selection. The suite's own endpoint_ids are not used here: they define which endpoints the suite's schedule covers, not which endpoint this request targets.
suite_id and probe_ids are mutually exclusive
Sending both returns 400 suite_id_conflicts_with_probe_ids. The two fields describe the same thing, and a request that carries a conflicting scope for a security scan is rejected rather than resolved by precedence. Pick one.
Errors
| Status | error | Cause |
|---|---|---|
| 400 | invalid_suite_id | suite_id is not a UUID |
| 400 | suite_id_conflicts_with_probe_ids | Both fields were supplied |
| 404 | suite_not_found | Unknown suite, or it belongs to another tenant |
| 422 | suite_has_no_probes | The suite exists but its probe list is empty |
An empty suite is refused rather than treated as "all probes". A caller naming a specific suite is asking for a specific selection, and expanding that to the full library would silently run a much larger scan than intended.
404 covers both an unknown id and a suite owned by another tenant, so this route cannot be used to test whether a suite id exists.
Worked example
Run every active suite against one endpoint:
set -euo pipefail
ENDPOINT=11a3dd55-5369-4020-8d11-2652c26fe0d1
curl -sS https://penaxtra.com/api/v2/suites -H "Authorization: Bearer $TOKEN" \
| jq -r '.data[] | select(.is_active and .probe_count > 0) | .id' \
| while read -r SUITE; do
curl -sS -X POST https://penaxtra.com/api/v2/scans \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d "{\"endpoint_id\":\"$ENDPOINT\",\"suite_id\":\"$SUITE\"}"
done
POST /api/v2/scans is limited to 6 calls per minute per token. Space the loop, or read retry_after_seconds from the 429 body and wait.
Related
Last reviewed: 2026-08-04. Reviewed by: Engineering. Content type: Developer documentation. Reach the maintainers: [email protected] .