Skip to content

API Reference

blackbuble edited this page Oct 30, 2026 · 1 revision

REST API Reference

Base URL: http://localhost:8080
Auth: Header X-API-Key: <key>
Interactive docs: http://localhost:8080/docs

Starting the server:

# Bare metal
python3 __main__.py --api --host 127.0.0.1 --port 8080

# Docker (the compose service does not publish a port, so publish it at run time)
docker compose run --rm -p 8080:8080 pentestai \
  python3 __main__.py --api --host 0.0.0.0

Auth

POST /api/v1/auth/keys

Create a new API key.

Request:

{"name": "my-client"}

Response:

{
  "id": "key_abc123",
  "name": "my-client",
  "key": "psk_f3a9c...",
  "created_at": "2024-01-15T10:00:00Z"
}

GET /api/v1/auth/keys

List all API keys.

DELETE /api/v1/auth/keys/{key_id}

Delete an API key.


Sessions

POST /api/v1/sessions

{
  "name": "htb-forest",
  "target": "10.10.11.162",
  "description": "HackTheBox Forest machine"
}

Response:

{
  "session_id": "a1b2c3d4",
  "name": "htb-forest",
  "target": "10.10.11.162",
  "created_at": "2024-01-15T10:00:00Z"
}

GET /api/v1/sessions

List all sessions. Query params: ?limit=20&offset=0

GET /api/v1/sessions/{session_id}

Full session details including services, findings, endpoints.

PATCH /api/v1/sessions/{session_id}

Update the target or notes.

DELETE /api/v1/sessions/{session_id}

Delete the session and all related files.

GET /api/v1/sessions/{session_id}/findings

List findings for the session. Query: ?severity=High&limit=50

POST /api/v1/sessions/{session_id}/findings

Add a finding manually.

{
  "title": "SQL Injection",
  "severity": "High",
  "target": "http://10.10.11.162/login",
  "tool": "manual",
  "description": "Login form vulnerable to SQLi",
  "recommendation": "Use parameterized queries"
}

DELETE /api/v1/sessions/{session_id}/findings/{finding_id}

Delete a finding.

POST /api/v1/sessions/{session_id}/validate

Run the exploit-validation gate over every finding.

{"validated": 4, "candidate": 7, "false_positive": 1}

GET /api/v1/sessions/{session_id}/report.sarif

Download findings as SARIF 2.1.0. Query: ?include_candidates=true to include unproven findings (marked validationStatus: candidate).

PATCH /api/v1/sessions/{session_id}/scope

Update the scope configuration.

{
  "allowed_ips": ["10.10.11.0/24"],
  "allowed_domains": ["forest.htb"],
  "active": true
}

Scans

POST /api/v1/scans

Start a new scan.

{
  "session_id": "a1b2c3d4",
  "target": "10.10.11.162",
  "scan_type": "recon",
  "options": {
    "deep": false,
    "web_scan": true
  }
}

scan_type: recon | web | sqli | dirs | killchain | react

Response:

{
  "scan_id": "scan_xyz789",
  "status": "running",
  "started_at": "2024-01-15T10:05:00Z"
}

GET /api/v1/scans/{scan_id}

Scan status.

{
  "scan_id": "scan_xyz789",
  "status": "running",
  "progress": "nmap scan in progress",
  "started_at": "...",
  "output_lines": 42
}

status: pending | running | completed | failed | cancelled

DELETE /api/v1/scans/{scan_id}

Cancel a running scan.

WebSocket /api/v1/scans/{scan_id}/stream

Live output stream.

const ws = new WebSocket(
  `ws://localhost:8080/api/v1/scans/${scanId}/stream`,
  [], {headers: {"X-API-Key": apiKey}}
);

ws.onmessage = (event) => {
  const msg = JSON.parse(event.data);
  if (msg.type === "output") {
    console.log(msg.line);
  } else if (msg.type === "done") {
    console.log("Scan complete");
  } else if (msg.type === "heartbeat") {
    // Keep-alive
  }
};

LLM

GET /api/v1/llm

Configured LLM status.

{
  "configured": true,
  "models": [
    {"provider": "anthropic", "model": "claude-sonnet-4-5", "tier": "complex", "active": true}
  ],
  "routing_enabled": true
}

POST /api/v1/llm/models

Add an LLM model.

{
  "provider": "groq",
  "model": "llama-3.1-70b-versatile",
  "api_key": "gsk_...",
  "tier": "simple"
}

POST /api/v1/llm/ask

Ask the LLM about the session.

{
  "session_id": "a1b2c3d4",
  "question": "What is the most likely attack vector for this target?"
}

Reports

POST /api/v1/reports

Generate a report.

{
  "session_id": "a1b2c3d4",
  "ai_enhanced": true,
  "format": "html"
}

Response:

{
  "report_id": "rpt_20240115",
  "format": "html",
  "size_bytes": 45231,
  "download_url": "/api/v1/reports/rpt_20240115/download"
}

GET /api/v1/reports/{report_id}/download

Download the report file.


Knowledge

GET /api/v1/knowledge/search

Search CVEs, techniques, and findings.

Query params: ?q=log4j+jndi&top_k=10

Response:

{
  "cves": [
    {
      "id": "CVE-2021-44228",
      "cvss": 10.0,
      "severity": "CRITICAL",
      "desc": "Log4j2 JNDI injection allows RCE"
    }
  ],
  "techniques": [...],
  "findings": [...]
}

GET /api/v1/knowledge/cve/{cve_id}

CVE details.


API Workflow Example

API_KEY="psk_..."
BASE="http://localhost:8080"

# 1. Create a session
SESSION=$(curl -s -X POST $BASE/api/v1/sessions \
  -H "X-API-Key: $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name":"forest","target":"10.10.11.162"}' | jq -r .session_id)

# 2. Start recon
SCAN=$(curl -s -X POST $BASE/api/v1/scans \
  -H "X-API-Key: $API_KEY" \
  -H "Content-Type: application/json" \
  -d "{\"session_id\":\"$SESSION\",\"target\":\"10.10.11.162\",\"scan_type\":\"recon\"}" \
  | jq -r .scan_id)

# 3. Wait for completion
while true; do
  STATUS=$(curl -s $BASE/api/v1/scans/$SCAN \
    -H "X-API-Key: $API_KEY" | jq -r .status)
  echo "Status: $STATUS"
  [[ "$STATUS" != "running" ]] && break
  sleep 5
done

# 4. Check findings
curl -s $BASE/api/v1/sessions/$SESSION/findings \
  -H "X-API-Key: $API_KEY" | jq .

# 5. Generate report
curl -s -X POST $BASE/api/v1/reports \
  -H "X-API-Key: $API_KEY" \
  -H "Content-Type: application/json" \
  -d "{\"session_id\":\"$SESSION\",\"ai_enhanced\":false}" | jq .

Clone this wiki locally