Evidence & audit API

HTTP reference for sealed capture artifacts, the engagement manifest, the hashed audit chain, finding provenance and retention policy.

All paths are relative to https://api.aleex-rank.ai/api/v2 and authenticate with X-API-Key: rk_... (see REST API). Rank keeps sealed capture artifacts, a signed engagement manifest, and a hashed audit chain as three separate systems. Finding provenance and the retention policy sit alongside them. Do not mix these up with triage evidence-files or the legacy operation-narrative list.

Sealed artifacts

GET /pentests/{id}/evidence
GET /pentests/{id}/evidence/{artifactId}

Each artifact is hashed at capture. kind is screenshot, http_exchange, tool_output, packet_capture or report. classification is internal, sensitive or credential. Reading an artifact is audited (an audit-chain entry is appended).

{
  "success": true,
  "data": {
    "items": [
      {
        "id": 17,
        "vulnerability_id": 42,
        "kind": "http_exchange",
        "classification": "sensitive",
        "status": "sealed",
        "sha256": "a3c19e0c8b7d4f1a2e6b90c4d8e1f0ab1234567890abcdef1234567890abcdef",
        "size_bytes": 18432,
        "mime_type": "application/json",
        "label": "boolean differential",
        "captured_at": "2026-03-02 10:14:22",
        "captured_by": "sqli-boolean/v1",
        "retention_until": "2027-03-02 10:14:22",
        "sealed_at": "2026-03-02 16:45:00"
      }
    ],
    "pagination": {"total": 1, "count": 1, "per_page": 20, "current_page": 1, "total_pages": 1},
    "pentest_id": 15
  }
}

Filter with ?vulnerability_id=, page and per_page. Listed statuses are sealed and purged.

GET …/evidence/{artifactId} returns {artifact, url, content_base64}. credential artifacts are inlined as base64 — there is no shareable URL. Every other classification returns a time-limited signed URL in url:

{
  "success": true,
  "data": {
    "artifact": {
      "id": 17,
      "vulnerability_id": 42,
      "kind": "screenshot",
      "classification": "sensitive",
      "status": "sealed",
      "sha256": "b7e20144aa90c1d2e3f4567890abcdef1234567890abcdef1234567890abcd",
      "size_bytes": 245760,
      "mime_type": "image/png",
      "label": null,
      "captured_at": "2026-03-02 10:15:01",
      "captured_by": "playwright",
      "retention_until": "2027-03-02 10:15:01",
      "sealed_at": "2026-03-02 16:45:00"
    },
    "url": "https://storage.googleapis.com/...",
    "content_base64": null
  }
}

A credential artifact sets url to null and fills content_base64:

{
  "success": true,
  "data": {
    "artifact": {
      "id": 18,
      "kind": "tool_output",
      "classification": "credential",
      "status": "sealed",
      "sha256": "c8f31255bb01d2e3f4567890abcdef1234567890abcdef1234567890abcd12",
      "size_bytes": 512,
      "mime_type": "text/plain",
      "captured_at": "2026-03-02 10:16:44"
    },
    "url": null,
    "content_base64": "LS0tLS1CRUdJTiBFWEFNUExFLS0tLS0KLi4u"
  }
}

Engagement manifest

GET /pentests/{id}/manifest

The sealed summary of the engagement: a merkle_root over the artifact hashes, whether the signature verifies, and when it was sealed. 404 until the pentest is sealed (This run has not been sealed yet).

{
  "success": true,
  "data": {
    "pentest_id": 15,
    "artifact_count": 12,
    "total_bytes": 1843200,
    "merkle_root": "f3a91c4e7b2d8801a6c5e9d0b4f7a1234567890abcdef1234567890abcdef12",
    "chain_seq": 140,
    "chain_hash": "9d4e2c1b8a706f5e4d3c2b1a0987654321fedcba9876543210abcdef12345678",
    "signature": "MEUCIQDx...",
    "signature_alg": "ECDSA_P256_SHA256",
    "sealed_at": "2026-03-02 16:45:00",
    "signature_valid": true,
    "artifacts_outside_seal": 0
  }
}

Audit chain

GET /pentests/{id}/audit-chain
GET /pentests/{id}/audit-chain/verify

The hashed control trail (RoE activation, kill, approvals, artifact reads) — not the capture artifacts themselves. Filter with event_type, date_from, date_to, page and per_page.

{
  "success": true,
  "data": {
    "events": [
      {
        "id": 901,
        "event_type": "kill_requested",
        "actor": "user:7",
        "payload": {"reason": "Out of authorized window"},
        "created_at": "2026-03-02 11:20:00",
        "chain_seq": 141,
        "entry_hash": "9d4e2c1b8a706f5e4d3c2b1a0987654321fedcba9876543210abcdef12345678"
      }
    ],
    "total": 142,
    "page": 1,
    "per_page": 50
  }
}

GET …/audit-chain/verify recomputes the whole hashed trail (not only this pentest’s slice) and reports whether it still adds up. The HTTP field is valid:

{
  "success": true,
  "data": {
    "valid": true,
    "checked": 142,
    "from_seq": 1,
    "to_seq": 142,
    "complete": true,
    "unsealed": 0,
    "anchors_verified": 3,
    "anchors_total": 3,
    "broken_at": null,
    "pentest_id": 15
  }
}

Finding provenance

GET /pentests/{id}/vulnerabilities/{vulnId}/provenance

Which agent, model and prompt produced the finding, which tools it invoked, and whether the stored evidence digest still matches.

{
  "success": true,
  "data": {
    "vulnerability_id": 42,
    "pentest_id": 15,
    "evidence_sha256": "e1a2b3c4d5e6f708192a3b4c5d6e7f8091a2b3c4d5e6f708192a3b4c5d6e7f80",
    "evidence_intact": true,
    "validation": {
      "status": "validated",
      "method": "sqli-boolean/v1",
      "validated_at": "2026-03-02 10:18:00",
      "confidence_score": 40,
      "confidence_rationale": "Boolean differential on the username parameter."
    },
    "origin": {
      "operation_id": 88,
      "agent_id": 101,
      "agent_role": "analysis",
      "model_id": 3,
      "model_alias": "gemini-2.5-flash",
      "provider": "google",
      "prompt_ref": "analysis/v3",
      "prompt_sha256": "aa11bb22cc33dd44ee55ff6677889900aa11bb22cc33dd44ee55ff6677889900",
      "tool_invocations": [{"tool": "http_request"}, {"tool": "screenshot"}],
      "reasoning_digest": "ff00...",
      "discovered_at": "2026-03-02 10:14:18",
      "recorded_at": "2026-03-02 10:16:00"
    }
  }
}

origin is null when provenance was never recorded. evidence_intact is null when the finding has no stored digest.

Retention policy

GET /evidence/retention-policy
PUT /evidence/retention-policy
FieldTypeNotes
retention_daysintExpiry window for non-credential artifacts
credential_retention_daysintMust be ≤ retention_days

Omit query params to address the caller’s personal policy. Pass ?team_id= for a team. Changing the policy rewrites expiry of existing artifacts.

{"retention_days": 365, "credential_retention_days": 30}
{
  "success": true,
  "data": {
    "owner_type": "user",
    "owner_id": 7,
    "retention_days": 365,
    "credential_retention_days": 30,
    "is_default": false,
    "artifacts_reprofiled": 12
  }
}

artifacts_reprofiled is present on PUT only. A credential window longer than the general window is rejected with 400.

Legacy operation narratives

GET /pentests/{id}/evidences

This list is operation narratives (the agent steps that produced findings), not sealed artifacts. It is documented with comments on the Pentests API. Resolution screenshots and patch proof stay on the finding as evidence-files.

Where to go next