Skip to main content
Unified CRUD API for interacting with any database table. Returns raw JSON and supports pagination, filtering, sorting, column selection, and full-text search across all tables. Read endpoints (GET) require viewer role or above. Write endpoints (POST, PUT, DELETE) require admin role. Project-scoped tables (scans, http_records, findings, source_repos, session_hostnames, oast_interactions, scan_logs, agent_runs, scopes) automatically filter by the X-Project-UUID header. Pass ?all_projects=true to disable project scoping (admin use).

GET /api/db/tables — List All Tables

Returns all database tables with their row counts.
curl -s http://localhost:9002/api/db/tables | jq .
{
  "tables": [
    { "name": "agent_runs", "row_count": 42 },
    { "name": "findings", "row_count": 1567 },
    { "name": "http_records", "row_count": 24301 },
    { "name": "scans", "row_count": 15 },
    { "name": "source_repos", "row_count": 3 }
  ],
  "total": 12
}

GET /api/db/tables/:table/columns — List Table Columns

Returns column metadata and primary key information for a specific table.
# Get columns for the findings table
curl -s http://localhost:9002/api/db/tables/findings/columns | jq .
{
  "table": "findings",
  "columns": [
    { "name": "id", "type": "INTEGER", "nullable": "no" },
    { "name": "project_uuid", "type": "TEXT", "nullable": "no" },
    { "name": "module_id", "type": "TEXT", "nullable": "yes" },
    { "name": "severity", "type": "TEXT", "nullable": "yes" },
    { "name": "created_at", "type": "DATETIME", "nullable": "no" }
  ],
  "primary_key": ["id"],
  "total": 22
}
Error responses:
CodeCondition
404Table name not found in database

GET /api/db/tables/:table/records — List Records

Returns paginated, filtered, sorted records from any table. Query parameters:
ParameterTypeDefaultDescription
limitint100Number of records to return (max 1000)
offsetint0Offset for pagination
sortstringColumn name to sort by (validated against table schema)
orderstringdescSort order: asc or desc
columnsstringComma-separated column whitelist (only return these columns)
searchstringFuzzy search across all text/varchar columns
truncateint0Truncate large text/binary fields to N characters (0 = full)
all_projectsstringfalseSet to true to disable automatic project_uuid filtering
filter.<column>stringExact match filter on a column
filter.<column>__likestringSQL LIKE pattern match (% wildcards)
filter.<column>__gtstringGreater than comparison
filter.<column>__gtestringGreater than or equal comparison
filter.<column>__ltstringLess than comparison
filter.<column>__ltestringLess than or equal comparison
filter.<column>__instringComma-separated IN list
filter.<column>__neqstringNot equal comparison
# List HTTP records with default pagination
curl -s http://localhost:9002/api/db/tables/http_records/records | jq .

# Filter findings by severity
curl -s 'http://localhost:9002/api/db/tables/findings/records?filter.severity__in=critical,high' | jq .

# Search across text fields
curl -s 'http://localhost:9002/api/db/tables/http_records/records?search=example.com' | jq .

# Select specific columns only
curl -s 'http://localhost:9002/api/db/tables/http_records/records?columns=uuid,hostname,method,path,status_code' | jq .

# Sort by response time, ascending
curl -s 'http://localhost:9002/api/db/tables/http_records/records?sort=response_time_ms&order=asc' | jq .

# Combine filters: GET requests with status > 399
curl -s 'http://localhost:9002/api/db/tables/http_records/records?filter.method=GET&filter.status_code__gt=399' | jq .

# LIKE filter with wildcards
curl -s 'http://localhost:9002/api/db/tables/http_records/records?filter.hostname__like=%25example%25' | jq .

# Paginate through results
curl -s 'http://localhost:9002/api/db/tables/http_records/records?limit=50&offset=100' | jq .

# Truncate large fields for listing views
curl -s 'http://localhost:9002/api/db/tables/http_records/records?truncate=200' | jq .

# List all agent runs across all projects
curl -s 'http://localhost:9002/api/db/tables/agent_runs/records?all_projects=true' | jq .

# List scan logs for a specific scan
curl -s 'http://localhost:9002/api/db/tables/scan_logs/records?filter.scan_uuid=abc-123&sort=created_at&order=asc' | jq .
{
  "table": "http_records",
  "total": 4521,
  "limit": 100,
  "offset": 0,
  "columns": ["uuid", "hostname", "method", "path", "status_code", "..."],
  "records": [
    {
      "uuid": "rec-0001-aaaa-bbbb-cccc",
      "hostname": "example.com",
      "method": "GET",
      "path": "/api/users",
      "status_code": 200
    }
  ]
}
Error responses:
CodeCondition
400Invalid query parameter or filter
404Table not found

GET /api/db/tables/:table/records/:id — Get Single Record

Returns a single record by its primary key value. Only works for tables with a single-column primary key.
# Get a specific HTTP record by UUID
curl -s http://localhost:9002/api/db/tables/http_records/records/rec-0001-aaaa-bbbb-cccc | jq .

# Get a specific finding by ID
curl -s http://localhost:9002/api/db/tables/findings/records/42 | jq .
{
  "table": "findings",
  "record": {
    "id": 42,
    "project_uuid": "00000000-0000-0000-0000-000000000001",
    "module_id": "xss-reflected",
    "module_name": "Reflected XSS",
    "severity": "high",
    "confidence": "confirmed",
    "matched_at": "https://example.com/search?q=test",
    "created_at": "2026-03-20T14:30:00Z"
  }
}
Error responses:
CodeCondition
400Table not found or composite PK (not supported)
404Record not found

POST /api/db/tables/:table/records — Create Record

Inserts a new record into the specified table. Requires admin role. The request body is a JSON object where keys are column names and values are the data to insert. Column names are validated against the table schema. For project-scoped tables, project_uuid is automatically injected from the X-Project-UUID header if not provided in the body.
# Create a scope rule
curl -s -X POST http://localhost:9002/api/db/tables/scopes/records \
  -H 'Content-Type: application/json' \
  -d '{
    "name": "include-example",
    "rule_type": "include",
    "host_pattern": "*.example.com",
    "enabled": true
  }' | jq .
{
  "table": "scopes",
  "message": "record created"
}
Error responses:
CodeCondition
400Invalid JSON, invalid column name, or empty fields
500Database constraint violation

PUT /api/db/tables/:table/records/:id — Update Record

Updates one or more fields on an existing record. Requires admin role. Only the fields included in the request body are updated (partial update). Primary key columns cannot be updated. Column names are validated against the table schema.
# Update a finding's severity
curl -s -X PUT http://localhost:9002/api/db/tables/findings/records/42 \
  -H 'Content-Type: application/json' \
  -d '{"severity": "critical"}' | jq .

# Update an HTTP record's risk score and remarks
curl -s -X PUT http://localhost:9002/api/db/tables/http_records/records/rec-0001-aaaa-bbbb-cccc \
  -H 'Content-Type: application/json' \
  -d '{"risk_score": 85, "remarks": "manually verified"}' | jq .
{
  "table": "findings",
  "id": "42",
  "message": "record updated"
}
Error responses:
CodeCondition
400Invalid JSON, invalid column, or attempt to update PK
404Record not found

DELETE /api/db/tables/:table/records/:id — Delete Record

Deletes a single record by primary key. Requires admin role.
# Delete a finding
curl -s -X DELETE http://localhost:9002/api/db/tables/findings/records/42 | jq .

# Delete a scan log entry
curl -s -X DELETE http://localhost:9002/api/db/tables/scan_logs/records/100 | jq .
{
  "table": "findings",
  "id": "42",
  "message": "record deleted"
}
Error responses:
CodeCondition
404Record not found
500Database error