Developer docs
CI/CD API Reference
Trigger attack simulations from your pipeline and fail builds on confirmed findings. Available on Professional and Business plans.
Plan differences in CI/CD
Professionalplan5,000 API triggers per day. Runs passive checks, protocol attacks (CORS, Host Header, HTTP Smuggling, WebSocket), JWT tests, injection testing, authentication bypass, credential stuffing, business logic, IDOR (ID enumeration, horizontal privilege escalation), XXE, and file upload probes. AI-guided tests and advanced injection chaining are not included — those require Business plan. Both single_url and full_domain (up to 1,000 pages, 4 per month) are available.
BusinessplanUnlimited API triggers per day. Runs the full test suite including injection, auth testing, login rate limit and lockout testing, business logic, IDOR, BOLA/BFLA multi-user authorization chains, vertical privilege escalation, advanced injection chaining, executable upload probing, and AI-guided exploration. Both single_url and full_domain (up to 5,000 pages) are available.
StarterplanNo API access. CI/CD integration requires Professional or Business plan.
Scan duration: single-URL scans complete in 5–15 minutes. Full-domain scans (Professional & Business) typically take 15–40 minutes depending on site size. Size your polling timeout accordingly — 30 minutes is a safe upper bound for single-URL, 60 minutes for full-domain.
Quick start
1. Create an API key in Settings → API Keys.
2. Ensure you have authorization to test every target URL. Typed consent and AUP agreement are required per scan. Optional DNS domain verification is available via Settings → Domains.
3. Trigger a scan and poll for results:
# Trigger a scan
RESP=$(curl -sf -X POST \
https://api.nautillo.pro/functions/v1/v1-scan-trigger \
-H "Content-Type: application/json" \
-H "X-API-Key: np_live_your_key_here" \
-d '{"target_url":"https://your-app.example.com","fail_on":"high"}')
SCAN_ID=$(echo "$RESP" | jq -r '.scan_id')
echo "Scan started: $SCAN_ID"
# Poll until done (single-URL scans finish in 5–15 min)
for i in $(seq 1 30); do
RESULT=$(curl -sf -H "X-API-Key: np_live_your_key_here" \
"https://api.nautillo.pro/functions/v1/v1-scan-status?scan_id=$SCAN_ID")
STATUS=$(echo "$RESULT" | jq -r '.status')
echo "[$i/30] $STATUS"
[ "$STATUS" = "completed" ] || [ "$STATUS" = "failed" ] || [ "$STATUS" = "timeout" ] && break
sleep 30
done
EXIT=$(echo "$RESULT" | jq -r '.exit_code // 2')
exit $EXITGitHub Actions
Native GitHub Action available
Use North-Human-AI/nautillo-pro-scan-action@v1 for a single-step integration. No curl, no polling logic, no shell scripting — just inputs and outputs. View on GitHub Marketplace →
Minimal setup — fail on High+
name: Security scan
on:
push:
branches: [main, staging]
jobs:
security:
runs-on: ubuntu-latest
steps:
- name: Nautillo Pro scan
uses: North-Human-AI/nautillo-pro-scan-action@v1
with:
api-key: ${{ secrets.NAUTILLO_API_KEY }}
url: https://staging.your-app.com
fail-on: highUsing outputs in later steps
- name: Nautillo Pro scan
id: scan
uses: North-Human-AI/nautillo-pro-scan-action@v1
with:
api-key: ${{ secrets.NAUTILLO_API_KEY }}
url: https://staging.your-app.com
fail-on: high
- name: Post report link to PR
if: always()
run: echo "Report ${{ steps.scan.outputs.report-url }}"
- name: Upload findings JSON
if: always()
run: echo '${{ steps.scan.outputs.findings-json }}'Authenticated scanning
Store login credentials as repository secrets. Use a dedicated test account — never personal credentials.
- name: Nautillo Pro scan (authenticated)
uses: North-Human-AI/nautillo-pro-scan-action@v1
with:
api-key: ${{ secrets.NAUTILLO_API_KEY }}
url: https://staging.your-app.com
fail-on: high
auth-type: form
auth-login-url: https://staging.your-app.com/login
auth-username: ${{ secrets.SCAN_TEST_EMAIL }}
auth-password: ${{ secrets.SCAN_TEST_PASSWORD }}Full-domain scan (Professional & Business)
- name: Nautillo Pro full-domain scan
uses: North-Human-AI/nautillo-pro-scan-action@v1
with:
api-key: ${{ secrets.NAUTILLO_API_KEY }}
url: https://staging.your-app.com
scan-type: full_domain
fail-on: high
timeout: '3600' # full-domain scans can take up to 40 minAction inputs
api-keystringAPI key from Settings → API Keys. Store as a repository secret.
urlstringTarget URL to scan. Must match a verified domain in your account.
scan-typestringsingle_url (default) or full_domain. full_domain available on Professional (4/month, 1,000 pages) and Business (30/month, 5,000 pages).
fail-onstringcritical (default) | high | medium | low | none
timeoutstringSeconds before the action times out. Default: 900. Use 3600 for full_domain.
auth-typestringbearer | basic | form. Omit for unauthenticated scans.
auth-tokenstringBearer token. Required when auth-type is bearer.
auth-usernamestringUsername. Required when auth-type is basic or form.
auth-passwordstringPassword. Required when auth-type is basic or form.
auth-login-urlstringLogin form URL. Required when auth-type is form.
Action outputs
scan-idstringUUID of the triggered scan.
report-urlstringURL to the full scan report on nautillo.pro.
statusstringpassed | failed | error
exit-codeinteger0 = pass, 1 = findings at threshold, 2 = scan error.
criticalintegerConfirmed Critical finding count.
highintegerConfirmed High finding count.
mediumintegerConfirmed Medium finding count.
findings-jsonstringJSON object: {"critical":0,"high":2,"medium":3,"low":1}
Credentials are encrypted with AES-256-GCM and automatically deleted within 24 hours of the scan completing. See the Privacy Policy for details.
GitHub Actions — raw API (no action dependency)
If you prefer not to use the action, or need a GitLab CI / Bitbucket Pipelines equivalent, use the curl-based pattern below. Store your API key as a CI secret named NAUTILLO_API_KEY.
name: Security scan (curl)
on:
push:
branches: [main, staging]
jobs:
security:
runs-on: ubuntu-latest
steps:
- name: Trigger Nautillo Pro scan
id: trigger
run: |
RESP=$(curl -sf -X POST \
https://api.nautillo.pro/functions/v1/v1-scan-trigger \
-H "Content-Type: application/json" \
-H "X-API-Key: ${{ secrets.NAUTILLO_API_KEY }}" \
-d '{"target_url":"https://staging.your-app.com","fail_on":"high"}')
echo "scan_id=$(echo "$RESP" | jq -r '.scan_id')" >> $GITHUB_OUTPUT
- name: Wait for results and check exit code
run: |
for i in $(seq 1 45); do
R=$(curl -sf -H "X-API-Key: ${{ secrets.NAUTILLO_API_KEY }}" \
"https://api.nautillo.pro/functions/v1/v1-scan-status?scan_id=${{ steps.trigger.outputs.scan_id }}")
S=$(echo "$R" | jq -r '.status')
echo "[$i] $S"
[ "$S" = "completed" ] || [ "$S" = "failed" ] || [ "$S" = "timeout" ] && break
sleep 30
done
echo "$R" | jq '{status,exit_code,findings_count,report_url}'
exit $(echo "$R" | jq -r '.exit_code // 2')Authenticated scanning (curl)
- name: Trigger authenticated scan
id: trigger
run: |
RESP=$(curl -sf -X POST \
https://api.nautillo.pro/functions/v1/v1-scan-trigger \
-H "Content-Type: application/json" \
-H "X-API-Key: ${{ secrets.NAUTILLO_API_KEY }}" \
-d "{
\"target_url\": \"https://staging.your-app.com\",
\"fail_on\": \"high\",
\"authentication\": {
\"auth_type\": \"form\",
\"credentials\": {
\"login_url\": \"https://staging.your-app.com/login\",
\"username\": \"$SCAN_USERNAME\",
\"password\": \"$SCAN_PASSWORD\"
}
}
}") || { echo "Trigger failed"; exit 1; }
echo "scan_id=$(echo "$RESP" | jq -r '.scan_id')" >> $GITHUB_OUTPUTCredentials are encrypted with AES-256-GCM and automatically deleted within 24 hours of the scan completing. See the Privacy Policy for details.
POST /v1-scan-trigger
Queues a new attack simulation. Returns immediately with a scan_id — poll /v1-scan-status for results.
Request body
target_urlstringFull URL to scan, including protocol. Must match a verified domain in your account. Example: https://staging.example.com
scan_typestringsingle_url (default) or full_domain. Full domain crawls all reachable pages up to 5,000 pages. Professional: 4/month (1,000 pages). Business: 30/month (5,000 pages).
fail_onstringSeverity threshold for CI/CD exit code 1. critical | high | medium | none (default). Exit code 1 when any confirmed finding meets or exceeds this severity.
authenticationobjectOptional. Login credentials for authenticated scanning. Supported auth types: form (username + password + login_url), bearer (token), basic (username + password). Credentials are encrypted with AES-256-GCM, stored server-side for the scan duration only (max 24 hours), then permanently deleted. Never logged or included in reports. Always pass credentials via environment variables or CI secrets — never hardcode them.
notify_webhookobjectOptional. Receive a signed HTTP POST when the scan completes, instead of polling. Format: {"url":"https://your-server.com/hook","secret":"optional-signing-secret"}. The request body is the same JSON as the /v1-scan-status response, with an X-Nautillo-Signature HMAC-SHA256 header for verification. If no secret is provided, one is generated and returned in the trigger response.
Response — 202 Accepted
{
"success": true,
"scan_id": "3f7a2b10-...",
"status": "pending",
"status_url": "https://api.nautillo.pro/functions/v1/v1-scan-status?scan_id=3f7a2b10-...",
"report_url": "https://nautillo.pro/report/3f7a2b10-...",
"queued_at": "2026-05-28T10:00:00.000Z",
"fail_on": "high"
}GET /v1-scan-status
Returns the current state of a scan. Poll every 30–60 seconds until status is completed, failed, or timeout. Typical duration: 5–15 minutes for single-URL, 15–40 minutes for full-domain.
Query parameters
scan_idstringUUID returned by /v1-scan-trigger.
Response — 200 OK
{
"success": true,
"scan_id": "3f7a2b10-...",
"status": "completed",
"exit_code": 1,
"fail_on": "high",
"findings_count": {
"critical": 0,
"high": 2,
"medium": 3,
"low": 1,
"info": 5
},
"report_url": "https://nautillo.pro/report/3f7a2b10-...",
"target_url": "https://staging.example.com",
"started_at": "2026-05-28T10:00:01.000Z",
"completed_at": "2026-05-28T10:08:43.000Z"
}status values
pendingstringScan is queued, not yet started.
runningstringScan is actively executing.
completedstringScan finished. Check exit_code for result.
failedstringScan encountered an internal error. Treat as exit code 2. Retry or contact support.
timeoutstringScan exceeded the maximum execution time. Treat as exit code 2.
exit_code values
0integerScan completed. No confirmed findings at or above fail_on threshold. Build passes.
1integerScan completed. One or more confirmed findings at or above threshold. Build should fail.
2integerScan did not complete (status is failed or timeout). Treat as infrastructure error — retry or alert.
nullnullScan is still running (status is pending or running). Keep polling.
Key management endpoints
These endpoints use JWT authentication (your session token), not an API key. They are used by the Nautillo Pro dashboard — call them directly only if you're building automation.
/api-key-createCreate a new API key. Returns the full key once — not stored in plaintext. Name it to identify which pipeline uses it.
{ "name": "GitHub Actions — staging" }/api-key-listList all active (non-revoked) API keys. Returns prefix and metadata — never the full key.
/api-key-revokeRevoke an API key immediately. Any pipeline using the key will fail until updated.
{ "key_id": "uuid-of-key" }Error responses
401HTTPMissing, invalid, or revoked API key.
403HTTPPlan too low for this operation (e.g. full_domain on Professional), or target URL violates the Acceptable Use Policy.
429HTTPDaily rate limit reached. Resets 24 hours after the first call in the window. Professional: 5,000 scans/day. Business: Unlimited.
400HTTPMissing or invalid parameters. Check the error field for details.
{
"success": false,
"error": "scan_type \"full_domain\" requires a Business plan. Upgrade at https://nautillo.pro/billing.",
"scan_type": "full_domain",
"upgrade_url": "https://nautillo.pro/billing"
}Security notes
- ✓Keys are stored as SHA-256 hashes — Nautillo Pro cannot recover a lost key. If you lose it, revoke and create a new one.
- ✓Keys may only be used to scan targets you own or have explicit authorization to test. Unauthorized use violates the Acceptable Use Policy and may violate computer fraud laws.
- ✓Revoke compromised keys immediately from Settings → API Keys.
- ✓Keys have no expiry by default. Rotate them periodically for long-lived pipelines.
- ✓Each key is rate-limited independently — use one key per pipeline so limits don't interfere.
- ✓Authentication credentials (passwords, tokens) passed to scans are encrypted with AES-256-GCM, stored server-side for the scan duration only (max 24 hours), then automatically and permanently deleted.
Need help setting up CI/CD integration? Contact support