Striker (VAPT)Integration API

Integration API

Pull Striker's scans, risks, reports, and attestations into your GRC platform with a REST API and signed webhooks.

Striker exposes a read API and signed webhooks so a GRC platform — NexusVoid's own Aegis, or any third-party GRC (Vanta, Drata, and similar) — can automatically ingest your scans, risks (findings), reports, and attestations. It is a plain REST + JSON API; nothing GRC-specific is required.

Base URL: https://vapt.nexusvoidai.com/api/v1

Authentication

Every request is authenticated with a Striker API key, sent as a bearer token. Create one in Dashboard → Settings → API Keys (the key is shown once — store it in your GRC's credential vault).

curl https://vapt.nexusvoidai.com/api/v1/scans \
  -H "Authorization: Bearer sk_live_..."

A key only ever sees the data of the account that created it. Responses never include internal fields (test credentials, payment IDs, storage keys, or hidden findings).

Connect with OAuth (one-click)

For a "Connect Striker" button in your GRC — no manual key copy-paste — Striker supports the OAuth 2.0 authorization-code flow. Clients are pre-registered (there is no self-serve registration): contact us to register your GRC and receive a client_id, client_secret, and your allow-listed redirect_uri(s).

The flow:

  1. Your app redirects the user to Striker's authorize URL:
https://vapt.nexusvoidai.com/oauth/authorize
  ?response_type=code
  &client_id=cli_...
  &redirect_uri=https://your-grc.example.com/oauth/striker/callback
  &scope=read
  &state=<random-csrf-token>
  &code_challenge=<pkce>            # optional (S256 recommended)
  &code_challenge_method=S256
  1. The user logs into Striker (if needed) and approves a consent screen. Striker redirects back to your exact redirect_uri with ?code=...&state=....

  2. Your backend exchanges the code for a token:

curl -X POST https://vapt.nexusvoidai.com/api/oauth/token \
  -u "cli_...:csec_..." \
  -d grant_type=authorization_code \
  -d code=code_... \
  -d redirect_uri=https://your-grc.example.com/oauth/striker/callback \
  -d code_verifier=<pkce-verifier>   # if you sent a challenge
{ "access_token": "sk_live_...", "token_type": "bearer", "scope": "read" }
  1. Store access_token and show Connected. Use it exactly like an API key — Authorization: Bearer <access_token> against /api/v1/*.

Notes:

  • redirect_uri must exactly match a registered URI; unmatched requests render an error instead of redirecting.
  • Always send and verify state (CSRF). Authorization codes are single-use and expire in 10 minutes.
  • The issued token is a scoped, read-only Striker API key. The user can revoke it anytime under Settings → API Keys → Connected apps.

Provider manifest (for GRC builders)

If your GRC uses a config-driven integration/provider registry, these are the values to register Striker as an OAuth provider:

FieldValue
Authorize URLhttps://vapt.nexusvoidai.com/oauth/authorize
Token URLhttps://vapt.nexusvoidai.com/api/oauth/token
Scopesread
PKCES256 (supported)
Client authHTTP Basic or body (client_id / client_secret)
Refresh tokensnone — the access token is long-lived (revoke to invalidate)
API basehttps://vapt.nexusvoidai.com/api/v1
Auth headerAuthorization: Bearer <access_token>

For the NexusVoid Aegis integration platform (@nexusvoid/integration-platform), the manifest is:

import type { IntegrationManifest } from '@nexusvoid/integration-platform';

export const strikerManifest: IntegrationManifest = {
  id: 'striker',
  name: 'NexusVoid Striker',
  description: 'Pull VAPT scans, risks, reports and attestations; start scans. Read-only.',
  category: 'Security',
  logoUrl: 'https://img.logo.dev/nexusvoidai.com?token=pk_replace_me',
  docsUrl: 'https://<docs-host>/vapt/integrations',
  auth: {
    type: 'oauth2',
    config: {
      authorizeUrl: 'https://vapt.nexusvoidai.com/oauth/authorize',
      tokenUrl: 'https://vapt.nexusvoidai.com/api/oauth/token',
      scopes: ['read'],
      pkce: true,
      clientAuthMethod: 'body',      // Basic ('header') also supported
      supportsRefreshToken: false,   // long-lived API-key token
    },
  },
  baseUrl: 'https://vapt.nexusvoidai.com/api/v1',
  capabilities: ['sync', 'webhook'],
  isActive: true,
};

The client_id / client_secret are configured as the connection credential (not in the manifest). Aegis sends redirect_uri = ${BASE_URL}/v1/integrations/oauth/callback, which must be registered on Striker for the client.

Conventions

  • List envelope: { "data": [ ... ], "next_cursor": "<id>" | null }. Pass ?cursor=<next_cursor> to page; ?limit= (default 25, max 100).
  • Single resource: { "data": { ... } }.
  • Errors: { "error": { "code": "...", "message": "..." } } with HTTP 401 (bad key), 404 (unknown/not yours), 422 (bad input).
  • Filtering: most lists accept ?since=<ISO-8601> and resource-specific filters (below).

Endpoints

List scans

GET /scans — filter by status, domain, since. Each item includes a severity_summary, findings_count, and has_reports.

Get a scan

GET /scans/{id} — full scan metadata plus executive_summary.

List findings (risks)

GET /findings — the risks feed across all scans. Filter by scan_id, severity, status, since. This is what most GRCs map into their risk register.

Get a finding

GET /findings/{id} — full detail: CVSS, CWE/CVE, OWASP, remediation, steps to reproduce, references.

Get report links

GET /reports/{scanId} — short-lived signed URLs (1 hour) for the technical, CXO, and compliance PDFs. Present only once a report is approved.

Remaining credits

GET /credits — { "scan_credits": <int> }. How many scans the account can still start.

Domains

GET /domains and GET /domains/{id} list domains with their verified status.

Run the full lifecycle

A GRC can drive add-domain → verify → start-scan → poll status → fetch findings and reports, all through the API.

1. Add a domain — POST /domains returns verification placements:

curl -X POST https://vapt.nexusvoidai.com/api/v1/domains \
  -H "Authorization: Bearer sk_live_..." \
  -H "content-type: application/json" \
  -d '{"domain":"example.com"}'
{ "data": {
  "domain": { "id": "dom_...", "domain": "example.com", "verified": false },
  "verification": {
    "dns_txt": { "type": "TXT", "host": "_vapt-verify.example.com", "value": "vapt-verify=<token>" },
    "file":    { "url": "https://example.com/.well-known/vapt-verify/<token>.txt", "content": "vapt-scan-verification=<token>" },
    "meta_tag":{ "tag": "<meta name="vapt-verify" content="<token>" />" }
  }
} }

2. Verify it — place one of the above, then POST /domains/{id}/verify with the method. Returns the domain with verified: true/false:

curl -X POST https://vapt.nexusvoidai.com/api/v1/domains/dom_.../verify \
  -H "Authorization: Bearer sk_live_..." \
  -H "content-type: application/json" \
  -d '{"method":"dns_txt"}'

3. Start a scan — requires a verified domain and consumes one scan credit:

curl -X POST https://vapt.nexusvoidai.com/api/v1/scans \
  -H "Authorization: Bearer sk_live_..." \
  -H "content-type: application/json" \
  -d '{"target_url":"https://app.example.com","environment":"production"}'
{ "data": { "scan_id": "scn_...", "target_domain": "app.example.com", "status": "pending", "credits_remaining": 2 } }

Returns 402 { "error": { "code": "insufficient_credits" } } when the account has no credits, or 403 domain_not_verified if the target is not under a verified domain. Optional body fields: tier (QUICK|STANDARD|DEEP|SINGLE), focus_areas, additional_context, and test_credentials ([{username, password, login_url?, auth_type?, role?}]) for authenticated scans.

4. Track it — poll GET /scans/{id} for status (pending → running → complete), then read GET /findings?scan_id=... for bugs (each with a status) and GET /reports/{scanId} for the PDFs.

Example: the risks feed

curl "https://vapt.nexusvoidai.com/api/v1/findings?severity=high&status=open" \
  -H "Authorization: Bearer sk_live_..."
{
  "data": [
    {
      "id": "fnd_123",
      "scan_id": "scn_456",
      "title": "IDOR on invoice export",
      "severity": "high",
      "cvss": 8.1,
      "cvss_vector": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N",
      "cwe": "CWE-639",
      "owasp": "A01:2021",
      "owasp_api": "API1:2023",
      "nist_csf": ["PR.AC"],
      "status": "open",
      "affected_url": "/api/invoices/export",
      "compliance": ["SOC2", "ISO27001"],
      "created_at": "2026-09-07T10:12:00.000Z"
    }
  ],
  "next_cursor": null
}

Webhooks

Instead of polling, subscribe to events so your GRC updates in near-real-time. Create an endpoint in Dashboard → Settings → Webhooks (or POST /api/v1/webhooks). The signing secret is returned once at creation.

Events:

EventFires when
scan.completedA scan is approved and complete (payload: the serialized scan)
scan.findings_readyFindings are finalized (payload: scan_id, severity_summary)
scan.report_readyReport PDFs are available
attestation.issuedA clean scan issues an attestation
attestation.revokedAn attestation is revoked

Each delivery is an HTTP POST with a JSON body { id, type, created_at, data } and headers:

  • X-Striker-Event — the event type
  • X-Striker-Delivery — a unique delivery id
  • X-Striker-Signature — t=<unix>,v1=<hmac_sha256>

Verify the signature

Recompute the HMAC over "<t>.<raw body>" with your endpoint secret and compare:

import crypto from "node:crypto";

function verify(rawBody, header, secret) {
  const parts = Object.fromEntries(header.split(",").map((p) => p.split("=")));
  const expected = crypto.createHmac("sha256", secret)
    .update(`${parts.t}.${rawBody}`).digest("hex");
  return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1));
}
import hmac, hashlib

def verify(raw_body: bytes, header: str, secret: str) -> bool:
    parts = dict(p.split("=") for p in header.split(","))
    expected = hmac.new(secret.encode(), f"{parts['t']}.".encode() + raw_body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, parts["v1"])

Reject deliveries whose t is older than a few minutes to prevent replay. Webhook URLs must be HTTPS and public (private, loopback, and link-local addresses are refused).

Mapping into a GRC

A typical ingestion:

  • Scans → assessment/scan records, keyed by id and target_domain.
  • Findings → your risk register (map severity, cvss, owasp, compliance[]; use status to track open vs resolved vs accepted risk).
  • Reports → evidence attachments (fetch the signed PDF URLs).
  • Attestations → a control/assurance signal, verifiable at verify_url.