Developer docs

CI/CD API Reference

Trigger attack simulations from your pipeline and fail builds on confirmed findings. Available on Professional and Business plans.

Base URL: https://api.nautillo.pro/functions/v1
Auth: X-API-Key header
Professional: 5,000 scans/day · Business: Unlimited

Plan differences in CI/CD

Professional
plan

5,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.

Business
plan

Unlimited 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.

Starter
plan

No 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 $EXIT

GitHub 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: high

Using 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 min

Action inputs

api-key
required
string

API key from Settings → API Keys. Store as a repository secret.

url
required
string

Target URL to scan. Must match a verified domain in your account.

scan-type
string

single_url (default) or full_domain. full_domain available on Professional (4/month, 1,000 pages) and Business (30/month, 5,000 pages).

fail-on
string

critical (default) | high | medium | low | none

timeout
string

Seconds before the action times out. Default: 900. Use 3600 for full_domain.

auth-type
string

bearer | basic | form. Omit for unauthenticated scans.

auth-token
string

Bearer token. Required when auth-type is bearer.

auth-username
string

Username. Required when auth-type is basic or form.

auth-password
string

Password. Required when auth-type is basic or form.

auth-login-url
string

Login form URL. Required when auth-type is form.

Action outputs

scan-id
string

UUID of the triggered scan.

report-url
string

URL to the full scan report on nautillo.pro.

status
string

passed | failed | error

exit-code
integer

0 = pass, 1 = findings at threshold, 2 = scan error.

critical
integer

Confirmed Critical finding count.

high
integer

Confirmed High finding count.

medium
integer

Confirmed Medium finding count.

findings-json
string

JSON 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_OUTPUT

Credentials 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_url
required
string

Full URL to scan, including protocol. Must match a verified domain in your account. Example: https://staging.example.com

scan_type
string

single_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_on
string

Severity threshold for CI/CD exit code 1. critical | high | medium | none (default). Exit code 1 when any confirmed finding meets or exceeds this severity.

authentication
object

Optional. 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_webhook
object

Optional. 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_id
required
string

UUID 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

pending
string

Scan is queued, not yet started.

running
string

Scan is actively executing.

completed
string

Scan finished. Check exit_code for result.

failed
string

Scan encountered an internal error. Treat as exit code 2. Retry or contact support.

timeout
string

Scan exceeded the maximum execution time. Treat as exit code 2.

exit_code values

0
integer

Scan completed. No confirmed findings at or above fail_on threshold. Build passes.

1
integer

Scan completed. One or more confirmed findings at or above threshold. Build should fail.

2
integer

Scan did not complete (status is failed or timeout). Treat as infrastructure error — retry or alert.

null
null

Scan 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.

POST
/api-key-create

Create 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" }
GET
/api-key-list

List all active (non-revoked) API keys. Returns prefix and metadata — never the full key.

POST
/api-key-revoke

Revoke an API key immediately. Any pipeline using the key will fail until updated.

{ "key_id": "uuid-of-key" }

Error responses

401
HTTP

Missing, invalid, or revoked API key.

403
HTTP

Plan too low for this operation (e.g. full_domain on Professional), or target URL violates the Acceptable Use Policy.

429
HTTP

Daily rate limit reached. Resets 24 hours after the first call in the window. Professional: 5,000 scans/day. Business: Unlimited.

400
HTTP

Missing 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