How Onyx works, end to end.
Everything from the moment you paste a spec URL to the final severity-scored result streaming to your dashboard. The full pipeline, the safeguards, and the answers to the questions people ask most.
What is Onyx?
Onyx is an AI-native API penetration-testing platform. You provide a link to any API's documentation, an OpenAPI / Swagger specification, and it autonomously verifies ownership, parses every endpoint, generates schema-aware attack payloads with Google Gemini 2.5 Flash, fires them through a rate-limited distributed queue, and streams severity-scored results back live.
Unlike a generic fuzzer, Onyx reads your actual schema: request bodies, path parameters, query strings, and asks an LLM to reason about what would break each specific endpoint. The result is context-sensitive coverage across the OWASP Top 10 without writing a single test case by hand.
Quick start
Five steps take you from sign-up to a live, scored attack run. Each one is covered in more depth further down.
Create an account
Sign up with an email and password. The Free plan needs no credit card and gives you 5 runs a month.
Paste a spec URL
Drop your OpenAPI / Swagger URL into the dashboard. Onyx parses every endpoint the moment it loads.
Verify the domain
Prove ownership once via a file or DNS record. The Execute Run button unlocks the instant it passes.
Execute the run
Onyx generates payloads, queues them, and fires. Watch results stream in live, no refresh needed.
Review & export
Triage findings by CVSS severity, drill into any endpoint, and export a PDF report on Pro and Team.
Onyx runs entirely from the spec URL. There's nothing to install on your servers and no SDK to wire in. If your API publishes an OpenAPI document, you're ready.
What you can paste
Every modern API ships a machine-readable spec describing its endpoints, parameters, and response formats, written in OpenAPI (formerly Swagger). Most APIs expose it at a path like /openapi.json, /swagger.json, or /v2/api-docs.
Valid targets
- petstore.swagger.io/v2/swagger.json
- api.example.com/openapi.json
- httpbin.org/spec.json
Blocked
- google.com (HTML, not a spec)
- 192.168.1.5/api (private IP, SSRF)
- any domain you don't own
Onyx performs DNS resolution on every URL. Any hostname resolving to 127.x, 10.x, 172.16-31.x, 192.168.x, 169.254.x, .local, or .internal is instantly blocked.
Can't find your spec URL?
If your team uses Swagger UI, the spec is the JSON the UI loads. Open the network tab and look for the .json request, or try appending one of these common paths to your API's base URL:
/openapi.json
/swagger.json
/v2/api-docs
/v3/api-docs
/swagger/v1/swagger.jsonDomain ownership verification
Before Onyx fires a single payload, you must prove you own the target domain. Without it, anyone could point Onyx at someone else's API. Verification closes that liability and is required once per domain, per account.
Host a file containing only your verification token at this exact path. No quotes, no extra whitespace, nothing else:
https://your-domain.com/.well-known/onyx-verify.txt
onyx-verify-a3f9c2b1...Once verified, a domain stays verified for your account. You can delete the file or DNS record afterward. DNS can take a few minutes to propagate; if a check fails right after saving the record, wait and retry.
What actually happens
Onyx extracts the hostname from your URL, generates a unique onyx-verify-<token>, and stores it against your account. When you click Check Verification, it probes the file over HTTPS first (retrying over HTTP if the connection fails), then falls back to a DNS TXT lookup. Whichever it finds first wins, and the run button unlocks.
Until a domain is verified, every attempt to scan it is rejected before a single request leaves the server:
POST /api/test-runs -> 403 Forbidden
{
"error": "DOMAIN_NOT_VERIFIED",
"domain": "target-api.com",
"message": "You must verify ownership of
\"target-api.com\" before scanning it."
}The attack flow
End to end, from clicking Execute Run to receiving live results:
- 01
Parse. The spec is fetched with WAF-bypass headers, then every endpoint, parameter, and request body is extracted.
- 02
Generate. Gemini crafts up to 20 schema-aware payloads per endpoint (with a 35-payload static fallback).
- 03
Queue. Payloads are added to a BullMQ Redis queue: 5 concurrent workers, 10 jobs/second, 10s timeout each.
- 04
Fire. Each worker re-checks SSRF, fires the payload, and records status code, latency, and a response snippet.
- 05
Score. Every result is classified with CVSS-inspired severity and folded into an overall 0-100 API security score.
- 06
Stream. Results broadcast live to your dashboard over WebSockets as each attack completes.
Run status phases
As a run progresses, its status moves through four phases, and the dashboard reflects each one live:
Attack types generated
Gemini produces strictly-typed JSON payloads across eight categories, each tuned to the endpoint under test:
SQL_INJECTION
Classic, blind, UNION, time-based, stacked, auth bypass
XSS
Reflected, stored, DOM, polyglot, event-handler vectors
MISSING_AUTH
Forged JWT (alg:none), missing headers, privilege escalation
PATH_TRAVERSAL
../, URL-encoded, double-dot, Windows paths
BOUNDARY
Null bytes, integer overflow, empty strings, NaN
OVERSIZED_PAYLOAD
Array bombs (10k elements), deeply nested JSON
TYPE_CONFUSION
Strings where ints expected, arrays where strings expected
RATE_LIMIT
Burst markers to probe rate-limiting behaviour
Live results
HTTP polling is too slow for watching attacks land. Onyx opens an authenticated WebSocket and streams every result the instant a worker records it, and the dashboard's metrics, charts, and endpoint rows update without a refresh.
Each event carries the method, endpoint, payload, status code, latency, and computed severity. A typical ATTACK_RESULT frame looks like this:
{
"type": "ATTACK_RESULT",
"data": {
"method": "POST",
"endpoint": "/users/login",
"statusCode": 500,
"latency": 412,
"attackType": "SQL_INJECTION",
"severity": "CRITICAL"
}
}Per-run isolation
Each run has its own subscriber set, so you only ever receive your own results.
Heartbeat
Ping/pong every 30s drops dead connections so the stream stays clean.
Ownership-checked
The server verifies you own a run in the database before allowing a subscription.
CVSS severity scoring
Each result is classified by status code and response content. The overall score starts at 100 and deducts per finding, giving you a single 0-100 number to track.
The final number maps to a label: CLEAN (100), LOW (76-99), MEDIUM (51-75), HIGH (26-50), and CRITICAL (0-25).
Security & resilience
Every run is fenced by layered guards, checked before and during the attack, so Onyx only ever hits the API you own.
SSRF guard
DNS resolution on every URL before any outbound request. Loopback, private, link-local, and cloud-metadata ranges are blocked at both spec-fetch and attack time.
Domain ownership gate
Every run checks for a verified (user, domain) record. No verified record returns a 403 before anything fires.
Rate limiting
Auth endpoints: 5 req/min per IP. Attack endpoints: 5 req/hour per IP. Plan quotas enforced per calendar month.
JWT security
7-day token expiry; the server hard-crashes on boot if JWT_SECRET is missing, preventing weak-key deployments.
WAF bypass (fetch only)
Spec fetch spoofs a real Chrome user-agent so Cloudflare/AWS WAFs don't false-negative during parsing.
Graceful AI fallback
If Gemini fails, 35 static payloads keep every scan completing, so the pipeline never stalls.
Architecture
Onyx is a decoupled pipeline: a React client, a guarded Express API, an AI payload stage, a Redis-backed queue, and a worker pool, with Postgres for persistence and WebSockets for telemetry. Each stage hands off to the next, so a slow target never blocks the UI.
- 01
Client. React + Vite on Vercel. Submits the spec URL and subscribes to the run over a WebSocket.
- 02
API + guards. Express on Render. Auth, rate-limit, quota, domain gate, and SSRF checks run before anything fires.
- 03
Gemini. Generates the payload set per endpoint, with a static fallback if the model is unavailable.
- 04
BullMQ + Redis. A job queue fans payloads out to 5 concurrent workers at 10/sec, surviving restarts.
- 05
Workers. Fire each payload, score the response by CVSS severity, and persist it.
- 06
Postgres + WS. Results are written to Neon Postgres via Prisma and broadcast live to the browser.
Built on React 18, Express 4, Prisma 7 + Neon Postgres, BullMQ 5 + Redis, Google Gemini 2.5 Flash, and Razorpay for billing.
Plans & quotas
Quotas are checked per calendar month before a run is created. Exceeding a limit returns a 429 QUOTA_EXCEEDED with an upgrade link.
| Plan | Price | Runs | Endpoints | |
|---|---|---|---|---|
| Free | $0 | 5 / mo | 10 / run | - |
| Pro | $9 / mo | 100 / mo | 50 / run | |
| Team | $18 / mo | 500 / mo | Unlimited |
Billing runs through Razorpay with webhook-verified plan upgrades, with no manual provisioning. Prices are charged in INR (₹900 / ₹1800 per month).
Frequently asked
Yes. Onyx requires domain ownership verification (via a file probe or DNS TXT record) before it will fire a single payload at any target. This is both a legal protection and a trust layer.
Ready to break your API?
Start free. No credit card required.
Glossary
Plain-English definitions for the terms used throughout these docs.
- OpenAPI / Swagger
- A machine-readable description of an API's endpoints, parameters, and responses. Onyx's only required input.
- SSRF
- Server-Side Request Forgery, which tricks a server into calling internal resources. Onyx blocks it via DNS resolution on every URL.
- CVSS
- Common Vulnerability Scoring System. The industry standard Onyx's severity labels are inspired by.
- BullMQ
- A Redis-backed job queue. Onyx uses it to fire attacks concurrently without overwhelming the target or Node.js.
- WebSocket
- A persistent two-way connection. Onyx streams every attack result over it so the dashboard updates in real time.
- Domain verification
- Proof that you own a target, via a file probe or DNS TXT record, required once before scanning.