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:
- 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
-
The user logs into Striker (if needed) and approves a consent screen. Striker redirects back to your exact
redirect_uriwith?code=...&state=.... -
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" }
- Store
access_tokenand show Connected. Use it exactly like an API key —Authorization: Bearer <access_token>against/api/v1/*.
Notes:
redirect_urimust 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:
| Field | Value |
|---|---|
| Authorize URL | https://vapt.nexusvoidai.com/oauth/authorize |
| Token URL | https://vapt.nexusvoidai.com/api/oauth/token |
| Scopes | read |
| PKCE | S256 (supported) |
| Client auth | HTTP Basic or body (client_id / client_secret) |
| Refresh tokens | none — the access token is long-lived (revoke to invalidate) |
| API base | https://vapt.nexusvoidai.com/api/v1 |
| Auth header | Authorization: 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.
List attestations
GET /attestations and GET /attestations/{id} — issued attestations with a verify_url.
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:
| Event | Fires when |
|---|---|
scan.completed | A scan is approved and complete (payload: the serialized scan) |
scan.findings_ready | Findings are finalized (payload: scan_id, severity_summary) |
scan.report_ready | Report PDFs are available |
attestation.issued | A clean scan issues an attestation |
attestation.revoked | An 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 typeX-Striker-Delivery— a unique delivery idX-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
idandtarget_domain. - Findings → your risk register (map
severity,cvss,owasp,compliance[]; usestatusto track open vs resolved vs accepted risk). - Reports → evidence attachments (fetch the signed PDF URLs).
- Attestations → a control/assurance signal, verifiable at
verify_url.
Related
- Capabilities — the data model behind scans and findings.
- Trust & Attestations — what an attestation certifies.