REST API
Authentication, endpoints, objects and errors. With copy-paste examples that run.
The HAS API gives your team programmatic access to your company's vulnerabilities, pentests and dashboard. Use it to push findings into your SIEM, open tickets in your backlog, or close the remediation loop from your own workflow.
https://api.hackersec.com/v1
api.hackersec.com/v1/openapi.json. Import it into Postman or Insomnia to get every endpoint ready, or use it to generate a client in your language.Authentication
Every request carries your secret key in the Authorization header:
curl https://api.hackersec.com/v1/dashboard \
-H "Authorization: Bearer hackersec_has_sk_..."
Creating a key
- Under Settings → Integrations → API. The company master creates and revokes keys.
- The value is shown once, at creation. Store it in your team's secret manager. If you lose it, revoke it and create another.
- Each key belongs to one company.
- The screen shows each key's last use. A key idle for months is a key to revoke.
Scopes
| Scope | Can |
|---|---|
read | Query vulnerabilities, pentests and the dashboard. |
read_write | Everything read can, plus change status, request retests and trigger integrations. |
Scope is chosen at creation and holds for the life of the key. For an integration that only queries, such as a dashboard, SIEM or report, use read.
Conventions
- JSON in, JSON out. Dates in ISO-8601 UTC (
2026-08-24T14:02:11Z). - Every object carries
idandobject. - Every response carries a
Request-Idheader. Quote it when contacting support. - An unknown field returns 400, with the field name in
param.
Lists and pagination
Every listing returns the same envelope, newest first:
{
"object": "list",
"data": [ ... ],
"has_more": true,
"next_cursor": "5214"
}
Use limit (default 100, max 200) and pass next_cursor as starting_after for the next page. While has_more is true, there is more to fetch.
Endpoints
| Method | Path | What it does |
|---|---|---|
| GET | /v1/dashboard | Counts by severity and status, plus a pentest summary |
| GET | /v1/vulnerabilities | List, with filters |
| GET | /v1/vulnerabilities/{id} | One vulnerability, with evidence and comments |
| GET | /v1/tests | List of pentests |
| GET | /v1/tests/{id} | One pentest, with scope |
| POST | /v1/vulnerabilities/{id}/status | Change status |
| POST | /v1/vulnerabilities/{id}/retest | Request a retest |
| POST | /v1/vulnerabilities/{id}/export | Send to the configured integration |
List vulnerabilities
Filters: severity, status, test, limit, starting_after. The values for severity and status are the same ones the response returns.
curl "https://api.hackersec.com/v1/vulnerabilities?severity=critical&status=reported" \
-H "Authorization: Bearer $HACKERSEC_KEY"
Change status
Accepted values: reported, in_correction, ignored, mitigated, retest.
curl -X POST https://api.hackersec.com/v1/vulnerabilities/16590818124833/status \
-H "Authorization: Bearer $HACKERSEC_KEY" \
-H "Content-Type: application/json" \
-d '{"status":"in_correction"}'
ignored and mitigated require justification, 50 to 2800 characters. These are your own declarations: one accepts the risk as is, the other states a compensating control exists. The text appears in the PDF report next to the vulnerability, so write the real decision.
curl -X POST https://api.hackersec.com/v1/vulnerabilities/16590818124833/status \
-H "Authorization: Bearer $HACKERSEC_KEY" \
-H "Content-Type: application/json" \
-d '{"status":"ignored","justification":"Endpoint reachable only through the corporate VPN with mandatory MFA. Risk accepted by the security committee on Aug 12."}'
Request a retest
curl -X POST https://api.hackersec.com/v1/vulnerabilities/16590818124833/retest \
-H "Authorization: Bearer $HACKERSEC_KEY" \
-H "Content-Type: application/json" \
-d '{"context":"Parameter now uses a parameterized query. Deployed to production today."}'
context is optional and helps: it tells whoever retests what changed. With a retest already pending for that vulnerability, the call returns 409, signalling that the previous request is still running.
Send to the integration
curl -X POST https://api.hackersec.com/v1/vulnerabilities/16590818124833/export \
-H "Authorization: Bearer $HACKERSEC_KEY" \
-H "Content-Type: application/json" \
-d '{"integration":"jira"}'
Omit integration or use "all" to fire every active integration. Requires an integration configured for the company and a master's key.
The Vulnerability object
| Field | Description |
|---|---|
id | The same identifier shown in the platform |
name | Technical type and short impact |
target | The exact asset where the flaw was confirmed |
severity | info, low, medium, high, critical |
status | reported, in_correction, fixed, ignored, retest, not_fixed, mitigated |
cvss_score, cvss_vector | CVSS 4.0 |
financial_impact | Estimated financial impact |
test | id of the pentest it was found in |
reported_at, fixed_at | Dates |
Fetching a single vulnerability also returns description, remediation, references, evidence (the proofs of concept) and comments.
The Test object
| Field | Description |
|---|---|
id, name | Pentest identifier and name |
level | ai_native or ai_first |
status | requested, in_progress, paused, retest, completed |
assets | Assets in scope |
vulnerabilities | Counts by severity |
created_at, started_at, ended_at | Dates |
Errors
{
"error": {
"type": "invalid_request_error",
"code": "justification_required",
"message": "A justification of at least 50 characters is required...",
"param": "justification"
},
"request_id": "req_28d675e64f3fe329"
}
| HTTP | When |
|---|---|
| 400 | Invalid JSON, unknown field or out-of-range value. The param field says which. |
| 401 | Key missing, invalid or revoked. |
| 403 | A read key on a write operation, or an expired contract. |
| 404 | The resource is out of reach for this key. |
| 409 | The operation conflicts with the current state. Example: a retest already pending. |