XnoleXDocs

API Reference

Complete reference for the XnoleX REST API. All endpoints return JSON and acceptapplication/json request bodies unless otherwise noted.

Base URL

All API requests should be directed to the following base URLs:

Development: http://localhost:8000
Production:  https://api.xnolex.com

All endpoints are prefixed with /api/v1.

Authentication

The API supports two authentication methods:

Bearer Token: Include the JWT access token in the Authorization header.

curl -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  http://localhost:8000/api/v1/targets

API Key: Include your API key in the X-API-Key header. Available on Monthly and Yearly plans.

curl -H "X-API-Key: xnk_your_api_key_here" \
  http://localhost:8000/api/v1/targets

Rate Limiting

Rate limits are applied per user based on subscription tier. Limits are measured in requests per minute (RPM).

TierRPM Limit
Demo (Free)10
Monthly Pro60
Yearly Pro120
Admin200

When the rate limit is exceeded, the API returns a 429 Too Many Requests status. Rate limit headers are included in all responses to help you track your usage.

Authentication

POST /api/v1/auth/login

Authenticate with email and password. No prior authentication required.

// Request
POST /api/v1/auth/login
Content-Type: application/json

{
  "email": "user@example.com",
  "password": "your_password"
}

// Response 200
{
  "access_token": "eyJhbGciOiJSUzI1NiIs...",
  "refresh_token": "eyJhbGciOiJSUzI1NiIs...",
  "token_type": "bearer",
  "user": {
    "id": "usr_abc123",
    "email": "user@example.com",
    "full_name": "John Doe",
    "role": "user",
    "tier_slug": "demo"
  }
}

POST /api/v1/auth/refresh

Exchange a refresh token for a new token pair. The old refresh token is invalidated.

// Request
POST /api/v1/auth/refresh
Content-Type: application/json

{
  "refresh_token": "eyJhbGciOiJSUzI1NiIs..."
}

// Response 200
{
  "access_token": "eyJhbGciOiJSUzI1NiIs...",
  "refresh_token": "eyJhbGciOiJSUzI1NiIs..."
}

POST /api/v1/auth/logout

Revoke the current refresh token session. Requires Bearer authentication.

// Request
POST /api/v1/auth/logout
Authorization: Bearer ACCESS_TOKEN
Content-Type: application/json

{
  "refresh_token": "eyJhbGciOiJSUzI1NiIs..."
}

// Response 200
{
  "message": "Logged out successfully"
}

GET /api/v1/auth/me

Get the current authenticated user's profile. Requires Bearer authentication.

PUT /api/v1/auth/me

Update the current user's profile. Requires Bearer authentication. Accepts full_name query parameter.

PUT /api/v1/auth/me/password

Change the current user's password. Requires Bearer authentication.

// Request
PUT /api/v1/auth/me/password
Authorization: Bearer ACCESS_TOKEN
Content-Type: application/json

{
  "current_password": "old_password",
  "new_password": "new_secure_password"
}

// Response 200
{
  "message": "Password updated successfully"
}

Targets

GET /api/v1/targets

List all targets for the authenticated user. Requires Bearer authentication.

POST /api/v1/targets

Create a new target. Requires Bearer authentication.

// Request
POST /api/v1/targets
Authorization: Bearer ACCESS_TOKEN
Content-Type: application/json

{
  "domain": "example.com",
  "organization": "Example Corp"
}

// Response 201
{
  "id": "tgt_abc123",
  "domain": "example.com",
  "organization": "Example Corp",
  "created_at": "2026-01-15T10:30:00Z"
}

DELETE /api/v1/targets/{target_id}

Delete a target and all associated scan data. Requires Bearer authentication. User must own the target.

POST /api/v1/targets/import

Bulk import multiple targets. Requires Bearer authentication.

// Request
POST /api/v1/targets/import
Authorization: Bearer ACCESS_TOKEN
Content-Type: application/json

{
  "domains": ["example.com", "test.org", "sample.net"]
}

// Response 200
{
  "imported": 3,
  "failed": 0,
  "targets": [
    { "id": "tgt_a1", "domain": "example.com" },
    { "id": "tgt_a2", "domain": "test.org" },
    { "id": "tgt_a3", "domain": "sample.net" }
  ]
}

Scans

GET /api/v1/scans

List all scans. Optional query parameters: target_id and status. Requires Bearer authentication.

POST /api/v1/scans

Start a new scan. Requires Bearer authentication. The engine parameter defaults to full.

// Request
POST /api/v1/scans
Authorization: Bearer ACCESS_TOKEN
Content-Type: application/json

{
  "target_id": "tgt_abc123",
  "engine": "full"
}

// Response 201
{
  "id": "scn_xyz789",
  "target_id": "tgt_abc123",
  "engine": "full",
  "status": "queued",
  "created_at": "2026-01-15T10:35:00Z"
}

GET /api/v1/scans/{scan_id}

Get detailed results for a specific scan. Requires Bearer authentication.

// Request
GET /api/v1/scans/scn_xyz789
Authorization: Bearer ACCESS_TOKEN

// Response 200
{
  "id": "scn_xyz789",
  "target_id": "tgt_abc123",
  "domain": "example.com",
  "engine": "full",
  "status": "completed",
  "started_at": "2026-01-15T10:35:01Z",
  "completed_at": "2026-01-15T10:42:30Z",
  "findings": [
    {
      "id": "fnd_001",
      "severity": "high",
      "title": "SQL Injection in login form",
      "path": "/login",
      "tool": "nuclei",
      "description": "The login form is vulnerable to SQL injection..."
    }
  ],
  "summary": {
    "total_findings": 12,
    "critical": 1,
    "high": 3,
    "medium": 5,
    "low": 3,
    "info": 0
  }
}

POST /api/v1/scans/{scan_id}/stop

Stop a running scan. Requires Bearer authentication. User must own the scan.

GET /api/v1/scans/{scan_id}/intel

Get correlated intelligence data for a scan. Returns findings enriched with context from multiple tools. Requires Bearer authentication.

GET /api/v1/scans/{scan_id}/report

Generate and retrieve a formatted report for a scan. Available on Monthly and Yearly plans. Requires Bearer authentication.

Dashboard

GET /api/v1/dashboard/summary

Get aggregate dashboard summary including total targets, scans, findings, and recent activity. Requires Bearer authentication.

GET /api/v1/dashboard/charts

Get chart data for the last 30 days including scan counts, finding severity distribution, and trends. Requires Bearer authentication.

Schedules

GET /api/v1/schedules

List all scheduled scans. Requires Bearer authentication.

POST /api/v1/schedules

Create a new scheduled scan. Requires Bearer authentication.

// Request
POST /api/v1/schedules
Authorization: Bearer ACCESS_TOKEN
Content-Type: application/json

{
  "target_id": "tgt_abc123",
  "engine": "full",
  "cron_expression": "0 2 * * *"
}

// Response 201
{
  "id": "sch_def456",
  "target_id": "tgt_abc123",
  "engine": "full",
  "cron_expression": "0 2 * * *",
  "enabled": true,
  "next_run": "2026-01-16T02:00:00Z"
}

PUT /api/v1/schedules/{schedule_id}

Update an existing scheduled scan. Requires Bearer authentication. User must own the schedule.

DELETE /api/v1/schedules/{schedule_id}

Delete a scheduled scan. Requires Bearer authentication. User must own the schedule.

POST /api/v1/schedules/{schedule_id}/run-now

Immediately trigger a scheduled scan without waiting for the next cron interval. Requires Bearer authentication.

Notifications

GET /api/v1/notifications

List all notifications for the authenticated user. Requires Bearer authentication.

PUT /api/v1/notifications/{notification_id}/read

Mark a single notification as read. Requires Bearer authentication.

POST /api/v1/notifications/read-all

Mark all unread notifications as read. Requires Bearer authentication.

GET /api/v1/notifications/unread-count

Get the count of unread notifications. Requires Bearer authentication.

API Keys

POST /api/v1/api-keys

Create a new API key. Requires Bearer authentication. The raw key is only returned once at creation time.

// Request
POST /api/v1/api-keys
Authorization: Bearer ACCESS_TOKEN
Content-Type: application/json

{
  "name": "CI/CD Pipeline",
  "permissions": ["read", "scan"],
  "expires_in_days": 90
}

// Response 201
{
  "id": "key_abc123",
  "name": "CI/CD Pipeline",
  "key": "xnk_a1b2c3d4e5f6...",
  "permissions": ["read", "scan"],
  "expires_at": "2026-04-15T10:30:00Z",
  "created_at": "2026-01-15T10:30:00Z"
}
Warning
The API key is only shown once at creation time. Store it securely — it cannot be retrieved later.

GET /api/v1/api-keys

List all API keys for the authenticated user. Key values are masked. Requires Bearer authentication.

DELETE /api/v1/api-keys/{key_id}

Revoke and delete an API key. Requires Bearer authentication. User must own the key.

Subscriptions

GET /api/v1/subscriptions/tiers

List all available subscription tiers with their features and pricing. No authentication required.

GET /api/v1/subscriptions/current

Get the current user's active subscription details. Requires Bearer authentication.

POST /api/v1/subscriptions/checkout

Create a Stripe checkout session for a new subscription. Requires Bearer authentication.

// Request
POST /api/v1/subscriptions/checkout
Authorization: Bearer ACCESS_TOKEN
Content-Type: application/json

{
  "tier_slug": "monthly"
}

// Response 200
{
  "checkout_url": "https://checkout.stripe.com/pay/cs_...",
  "session_id": "cs_abc123"
}

POST /api/v1/subscriptions/upgrade

Upgrade to a different tier. Handled via Stripe with proration. Requires Bearer authentication.

POST /api/v1/subscriptions/cancel

Cancel the current subscription. Takes effect at the end of the billing period. Requires Bearer authentication.

GET /api/v1/subscriptions/usage

Get current usage statistics including scans used and targets used against tier limits. Requires Bearer authentication.

WebSocket

WS /api/v1/ws/scans/{scan_id}?token={jwt}

Connect to live scan progress via WebSocket. The JWT access token must be provided as a query parameter. Messages are sent as JSON with real-time progress updates.

// Connect to scan progress
const ws = new WebSocket(
  'ws://localhost:8000/api/v1/ws/scans/scn_xyz789?token=eyJhbGciOi...'
);

ws.onmessage = (event) => {
  const data = JSON.parse(event.data);
  console.log('Progress:', data.progress);
  console.log('Current tool:', data.current_tool);
  console.log('Findings so far:', data.findings_count);
};

ws.onclose = () => {
  console.log('Scan completed or connection closed');
};
Info
The WebSocket connection will automatically close when the scan completes. Attempting to connect to a scan you do not own will be rejected.