ScrutoraCode, cloud & consent

Developer Documentation

CI/CD Pipeline Integration

Automate HIPAA, GDPR, SOC 2, PCI DSS, DPDPA, and Singapore MGF compliance scanning in your CI/CD pipeline. Incremental PR scanning, pass/fail gating, and PR comments, all API-driven.

Quick Start

Get up and running in three steps.

1

Generate an API key

Go to Settings → API Keys and create a new key. Save it securely; you will only see it once.

2

Add key to your CI secrets

Store your API key as a secret in your CI/CD platform.

Secret name
SECUREHEALTH_API_KEY=sk_live_your_key_here
  • GitHub: Settings → Secrets → Actions → New secret
  • GitLab: Settings → CI/CD → Variables (masked + protected)
  • GitLab MR notes: Also add GITLAB_API_TOKEN with api scope
3

Add the scan step to your pipeline

Add a workflow step that zips your code, uploads it, and checks the result. See full examples below.

Minimal example
# 1. Zip your source code
zip -r codebase.zip . -x '.git/*' 'node_modules/*' 'venv/*'

# 2. Upload and start scan (with git context for incremental mode)
SCAN_ID=$(curl -s -X POST \
  -H "Authorization: Bearer $SECUREHEALTH_API_KEY" \
  -F "code=@codebase.zip" \
  "https://api.scrutora.com/api/v1/scans?frameworks=hipaa&scan_mode=auto&branch=main&commit_sha=$(git rev-parse HEAD)" \
  | jq -r '.scan_id')

# 3. Poll for results
while true; do
  STATUS=$(curl -s \
    -H "Authorization: Bearer $SECUREHEALTH_API_KEY" \
    "https://api.scrutora.com/api/v1/scans/$SCAN_ID" \
    | jq -r '.status')
  [ "$STATUS" = "completed" ] || [ "$STATUS" = "failed" ] && break
  sleep 10
done

# 4. Get exit code (0 = pass, 1 = fail)
EXIT=$(curl -s \
  -H "Authorization: Bearer $SECUREHEALTH_API_KEY" \
  "https://api.scrutora.com/api/v1/scans/$SCAN_ID/exit-code" \
  | jq -r '.exit_code')
exit $EXIT

API Reference

All endpoints require a valid API key in the Authorization header.

Authorization: Bearer sk_live_...
POST/api/v1/scans

Create a scan

Upload a zip archive and start a compliance scan. Supports multi-framework scanning, incremental mode, and git context for CI/CD.

Parameters

NameTypeDescription
frameworksqueryComma-separated: hipaa, gdpr, soc2. Default: hipaa
scan_modequeryfull, incremental, or auto. Default: full. Incremental requires Business+ AND a .git directory inside the uploaded archive: changed files are computed with git diff against base_branch. Without .git history the scan automatically falls back to a full scan.
commit_shaqueryGit commit SHA for incremental diff tracking
branchqueryBranch name (e.g. feature/auth)
pr_numberqueryPR number: enables incremental mode with auto
base_branchqueryTarget branch of the PR (e.g. main)
project_idqueryAssociate scan with a project
project_namequeryCreate or match project by name

Request

curl -X POST \
  "https://api.scrutora.com/api/v1/scans?frameworks=hipaa,gdpr&scan_mode=auto&commit_sha=a1b2c3d&branch=feature/auth&pr_number=42&base_branch=main" \
  -H "Authorization: Bearer sk_live_..." \
  -F "code=@codebase.zip"

Response

{
  "scan_id": "a1b2c3d4e5f6...",
  "status": "processing",
  "message": "Scan queued successfully. Poll GET /api/v1/scans/{scan_id} for status."
}
GET/api/v1/scans/{scan_id}

Get scan status

Check the status of a scan and view basic results when complete.

Parameters

NameTypeDescription
fail_on_criticalqueryConsider critical violations a failure. Default: true
fail_on_highqueryConsider high violations a failure. Default: false

Request

curl "https://api.scrutora.com/api/v1/scans/{scan_id}" \
  -H "Authorization: Bearer sk_live_..."

Response

{
  "scan_id": "a1b2c3d4e5f6...",
  "status": "completed",
  "violations": { "critical": 2, "high": 5, "medium": 8, "low": 3, "total": 18 },
  "passed": false,
  "risk_score": 45,
  "compliance_grade": "D",
  "report_url": "https://api.scrutora.com/api/v1/scans/a1b2c3d4.../report"
}
GET/api/v1/scans/{scan_id}/details

Get detailed scan status (incremental)

Extended status with incremental diff data. Returns new/resolved findings compared to the previous full scan baseline. Use this endpoint for PR comments and detailed CI gating.

Parameters

NameTypeDescription
fail_thresholdquerySeverity threshold: critical, high, medium, low, none. Default: high

Request

curl "https://api.scrutora.com/api/v1/scans/{scan_id}/details?fail_threshold=high" \
  -H "Authorization: Bearer sk_live_..."

Response

{
  "scan_id": "a1b2c3d4e5f6...",
  "status": "completed",
  "scan_mode": "incremental",
  "source": "cicd",
  "commit_sha": "a1b2c3d",
  "branch": "feature/auth",
  "pr_number": 42,
  "base_branch": "main",
  "files_scanned": 5,
  "files_total": 87,
  "changed_files": ["src/auth.py", "src/utils.py"],
  "score": 85.0,
  "violations_count": 3,
  "new_findings_count": 2,
  "resolved_findings_count": 1,
  "new_findings": [
    { "rule_id": "AUTH-001", "severity": "critical", "file": "src/auth.py", "line": 42, "message": "Missing access control" }
  ],
  "resolved_findings": [
    { "rule_id": "TRANS-001", "severity": "high", "file": "src/utils.py", "line": 15, "message": "Fixed: TLS now enforced" }
  ],
  "check_status": "fail",
  "check_message": "2 new HIGH+ finding(s) in required frameworks",
  "fail_threshold": "high",
  "passed": false,
  "report_url": "https://api.scrutora.com/api/v1/scans/a1b2c3d4.../report"
}
GET/api/v1/scans/{scan_id}/exit-code

Get CI exit code

Returns 0 for pass, 1 for fail. Use this to gate deployments in your CI pipeline.

Parameters

NameTypeDescription
fail_on_criticalqueryFail on critical violations. Default: true
fail_on_highqueryFail on high-severity violations. Default: false
min_scorequeryMinimum passing score (0-100). Default: 0

Request

curl "https://api.scrutora.com/api/v1/scans/{scan_id}/exit-code" \
  -H "Authorization: Bearer sk_live_..."

Response

{
  "exit_code": 1,
  "passed": false,
  "reason": "2 critical violation(s)"
}
GET/api/v1/scans/{scan_id}/report

Download PDF report

Download the full compliance report as a PDF. Supports HIPAA, GDPR, SOC 2, DPDPA, Singapore MGF, and PCI DSS frameworks.

Request

curl -OJ "https://api.scrutora.com/api/v1/scans/{scan_id}/report" \
  -H "Authorization: Bearer sk_live_..."

Response

# Returns PDF binary file
# Content-Type: application/pdf
GET/api/v1/scans/{scan_id}/sarif

Download SARIF results

Download scan results in SARIF v2.1.0 format. Compatible with GitHub Code Scanning, Azure DevOps, VS Code SARIF Viewer, and other static analysis tools.

Request

curl -OJ "https://api.scrutora.com/api/v1/scans/{scan_id}/sarif" \
  -H "Authorization: Bearer sk_live_..."

Response

{
  "$schema": "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/...",
  "version": "2.1.0",
  "runs": [{
    "tool": { "driver": { "name": "Scrutora", "rules": [...] } },
    "results": [
      {
        "ruleId": "AUTH-001",
        "level": "error",
        "message": { "text": "Missing access control check" },
        "locations": [{ "physicalLocation": { "artifactLocation": { "uri": "src/auth.py" } } }]
      }
    ]
  }]
}

CI/CD Examples

Copy-paste configurations for popular CI/CD platforms.

Incremental scan on every pull request. Posts a PR comment with results and fails the check if violations exceed the threshold.

.github/workflows/scrutora-pr-scan.yml
name: Scrutora PR Scan

on:
  pull_request:
    types: [opened, synchronize, reopened]

jobs:
  compliance-scan:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      pull-requests: write
    env:
      SECUREHEALTH_API_URL: https://api.scrutora.com
      FRAMEWORKS: hipaa
      FAIL_THRESHOLD: high
      POLL_TIMEOUT: 600
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Create archive
        run: zip -r codebase.zip . -x '.git/*' 'node_modules/*' 'venv/*' '__pycache__/*' '.next/*' 'dist/*' 'build/*'

      - name: Upload and scan
        id: scan
        run: |
          PARAMS="frameworks=$FRAMEWORKS&scan_mode=auto&commit_sha=${{ github.sha }}&branch=${{ github.head_ref }}"
          PARAMS="$PARAMS&pr_number=${{ github.event.pull_request.number }}&base_branch=${{ github.base_ref }}"

          RESPONSE=$(curl -s -X POST \
            -H "Authorization: Bearer ${{ secrets.SECUREHEALTH_API_KEY }}" \
            -F "code=@codebase.zip" \
            "$SECUREHEALTH_API_URL/api/v1/scans?$PARAMS")

          SCAN_ID=$(echo "$RESPONSE" | jq -r '.scan_id')
          echo "scan_id=$SCAN_ID" >> "$GITHUB_OUTPUT"
          echo "Scan started: $SCAN_ID"

      - name: Wait for completion
        id: result
        run: |
          SCAN_ID=${{ steps.scan.outputs.scan_id }}
          TIMEOUT=$POLL_TIMEOUT
          ELAPSED=0
          while [ $ELAPSED -lt $TIMEOUT ]; do
            DETAILS=$(curl -s \
              -H "Authorization: Bearer ${{ secrets.SECUREHEALTH_API_KEY }}" \
              "$SECUREHEALTH_API_URL/api/v1/scans/$SCAN_ID/details?fail_threshold=$FAIL_THRESHOLD")

            STATUS=$(echo "$DETAILS" | jq -r '.status')
            if [ "$STATUS" = "completed" ] || [ "$STATUS" = "failed" ]; then
              echo "details=$DETAILS" >> "$GITHUB_OUTPUT"
              echo "$DETAILS" | jq .
              break
            fi
            echo "Scanning... ($STATUS) — ${ELAPSED}s elapsed"
            sleep 15
            ELAPSED=$((ELAPSED + 15))
          done

      - name: Comment on PR
        uses: actions/github-script@v7
        with:
          script: |
            const details = JSON.parse('${{ steps.result.outputs.details }}');
            const mode = details.scan_mode === 'incremental' ? 'Incremental' : 'Full';
            const status = details.check_status === 'pass' ? '✅ Pass' :
                           details.check_status === 'fail' ? '❌ Fail' : '⚠️ Warning';

            let body = `## Scrutora Compliance Scan\n\n`;
            body += `| Metric | Value |\n|---|---|\n`;
            body += `| Score | ${details.score ?? 'N/A'} |\n`;
            body += `| Violations | ${details.violations_count ?? 0} |\n`;
            body += `| Scan Mode | ${mode} |\n`;
            body += `| Status | ${status} |\n`;

            if (details.new_findings_count > 0) {
              body += `\n### New Findings (${details.new_findings_count})\n`;
              for (const f of details.new_findings || []) {
                body += `- **${f.severity.toUpperCase()}** \`${f.rule_id}\` ${f.file}:${f.line} — ${f.message}\n`;
              }
            }
            if (details.resolved_findings_count > 0) {
              body += `\n### Resolved (${details.resolved_findings_count}) 🎉\n`;
            }

            body += `\n[View Full Report](${details.report_url})\n`;
            body += `---\n`;
            body += `Scanned by [Scrutora](https://scrutora.com) | Commit: ${context.sha.substring(0, 7)}`;

            // Update existing comment or create new one
            const comments = await github.rest.issues.listComments({
              owner: context.repo.owner,
              repo: context.repo.repo,
              issue_number: context.issue.number,
            });
            const existing = comments.data.find(c => c.body?.includes('Scrutora Compliance Scan'));
            if (existing) {
              await github.rest.issues.updateComment({
                owner: context.repo.owner,
                repo: context.repo.repo,
                comment_id: existing.id,
                body,
              });
            } else {
              await github.rest.issues.createComment({
                owner: context.repo.owner,
                repo: context.repo.repo,
                issue_number: context.issue.number,
                body,
              });
            }

      - name: Check result
        run: |
          CHECK=$(echo '${{ steps.result.outputs.details }}' | jq -r '.check_status')
          if [ "$CHECK" = "fail" ]; then
            echo "::error::Compliance check failed"
            exit 1
          fi

Configuration Variables

VariableDefaultDescription
FRAMEWORKShipaaComma-separated: hipaa, gdpr, soc2
FAIL_THRESHOLDhighSeverity gate: critical, high, medium, low, none
POLL_TIMEOUT600Max wait time in seconds. Increase for large repos.
SECUREHEALTH_API_URLhttps://api.scrutora.comAPI base URL. Override for self-hosted deployments.

GitLab MR notes: To post scan results as MR notes, add a GITLAB_API_TOKEN CI/CD variable with a project access token that has api scope. Go to Settings → CI/CD → Variables and mark it as masked + protected.

PR Comment Examples

Both GitHub and GitLab workflows post scan results directly on your PR/MR. Here's what the comments look like.

Passing scan

✅ Scrutora Compliance Scan

Score92
Violations3
Scan Modeincremental
Check Statuspass

All findings are below the configured severity threshold (high).

View full report·Scanned by Scrutora | Commit: abc1234

Failing scan

❌ Scrutora Compliance Scan

Score45
Violations12
Scan Modeincremental
Check Statusfail

Incremental Diff

  • New findings: 4
  • Resolved findings: 1

4 new findings at HIGH severity or above. Fix these before merging.

View full report·Scanned by Scrutora | Commit: def5678

Repo Configuration

Add a .scrutora.yml file to your repo root to configure scanning behavior without modifying API calls.

.scrutora.yml
# Compliance frameworks to scan
frameworks:
  - hipaa
  - gdpr

# Scan mode for PR builds (full or incremental)
pr_scan_mode: incremental

# Scan mode for pushes to main (full or incremental)
merge_scan_mode: full

# Severity threshold for PR failure (critical, high, medium, low, none)
fail_threshold: high

# Only fail on findings from these frameworks
required_frameworks:
  - hipaa

# ── Per-rule overrides ──────────────────────────────
rules:
  # Disable a rule entirely
  CONSENT-001:
    enabled: false

  # Override severity (critical, high, medium, low)
  TRANSFER-001:
    severity: low

  # Set minimum confidence threshold (violations below are dropped)
  RETENTION-001:
    min_confidence: 0.60

  # Combined overrides
  PII-001:
    severity: medium
    min_confidence: 0.70

# ── Global settings ─────────────────────────────────
settings:
  # Minimum confidence for any violation to be reported (0.0–1.0)
  min_confidence: 0.40

  # Glob patterns to exclude from scanning
  exclude_paths:
    - "tests/**"
    - "docs/**"
    - "migrations/**"
    - "src/generated/**"
    - "vendor/**"

Configuration Priority

Settings are resolved in this order (first match wins):

  1. 1. API query parameters (explicit request)
  2. 2. .scrutora.yml in the repo
  3. 3. Project-level defaults (set in dashboard)
  4. 4. Plan-based defaults (free = hipaa only)

Rule Override Options

Each rule in the rules: section supports:

  • enabled: Set to false to disable the rule
  • severity: Override: critical, high, medium, or low
  • min_confidence: Drop violations below this threshold (0.0–1.0)

Suppression File

Add a .scrutoraignore file to your repo root to suppress known false positives or accepted risks. Suppressed violations are still tracked but excluded from the report and CI gating.

.scrutoraignore
# Suppress a rule everywhere
rule:CONSENT-001

# Suppress all violations in a specific file
file:src/generated/models.py

# Suppress a rule only in a specific file
rule:PII-001  file:app/legacy_service.py

# Suppress a specific line
rule:SDLC-001  file:config.py  line:42

# Glob patterns work for file matching
file:vendor/**
file:*.generated.cs

Suppression Directives

Each line can contain one or more directives. Lines starting with # are comments.

  • rule:RULE-ID: Match by rule ID (e.g. AUTH-001, PII-001)
  • file:path/to/file.py: Match by file path (supports glob patterns)
  • line:42: Match by line number (must be combined with file:)

When multiple directives appear on one line, all must match for the violation to be suppressed.

GitHub PR Bot

Business

Automatically scan pull requests and post compliance results as PR comments. The PR Bot triggers on every PR opened, synchronized, or reopened, giving your team instant compliance feedback.

Webhook Setup

  1. 1. Go to your GitHub repo → Settings → Webhooks
  2. 2. Add webhook URL: https://api.scrutora.com/api/github/pr/webhook
  3. 3. Content type: application/json
  4. 4. Select events: Pull requests
  5. 5. Add your webhook secret (optional but recommended)

What Gets Posted

  • Score badge: pass / warning / fail with score
  • Severity breakdown: critical, high, medium, low counts
  • Top findings: up to 5 most important violations
  • File locations: exact files and line numbers
  • Framework coverage: HIPAA, GDPR, SOC 2, PCI DSS, DPDPA, and/or Singapore MGF
Manual PR Comment (API)
# Post scan results to an existing PR
curl -X POST "https://api.scrutora.com/api/github/pr/comment" \
  -H "Authorization: Bearer YOUR_JWT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "scan_id": "abc123",
    "repo_full_name": "your-org/your-repo",
    "pr_number": 42
  }'

Prerequisite: Connect your GitHub account at Dashboard → Scan and ensure you're on a Business or Enterprise plan.

SARIF Export

Export scan results in SARIF v2.1.0 (Static Analysis Results Interchange Format) for integration with GitHub Code Scanning, Azure DevOps, VS Code SARIF Viewer, and other CI/CD tools.

Download SARIF via API
# Download SARIF results after a scan completes
curl -OJ "https://api.scrutora.com/api/v1/scans/{scan_id}/sarif" \
  -H "Authorization: Bearer sk_live_..."

# Upload to GitHub Code Scanning (in GitHub Actions)
- name: Upload SARIF
  uses: github/codeql-action/upload-sarif@v3
  with:
    sarif_file: results.sarif

GitHub Code Scanning

Upload SARIF to see compliance violations as code scanning alerts directly in your GitHub repository.

Azure DevOps

Import SARIF results into Azure DevOps for centralized security dashboard and build gating.

VS Code

Open SARIF files in the VS Code SARIF Viewer extension to navigate violations inline in your editor.

Incremental Scanning

On Business and Enterprise plans, PR scans only analyze changed files and their dependencies, cutting scan time significantly for large codebases.

How It Works

  1. 1. Git diff detects files changed between your PR branch and base branch
  2. 2. Dependency closure expands to include files that import the changed files (1 level)
  3. 3. Only the closure files are scanned
  4. 4. Results are merged with the latest full scan baseline
  5. 5. New and resolved findings are computed as a diff

Force Full Scan Conditions

The scanner automatically upgrades to a full scan when:

  • • No previous full scan baseline exists
  • .scrutora.yml was modified in the PR
  • • More than 30% of files changed
  • • Last full scan is older than 30 days
  • • Push to main/master (always full)
  • • Explicitly requested via scan_mode=full

Plan requirement: Incremental scanning is available on Business and Enterprise plans. On Team/Free plans, scan_mode=auto silently falls back to a full scan.

Best Practices

Store keys as secrets

Never hardcode API keys in your code. Use your CI system's secret management (GitHub Secrets, GitLab Variables, etc.).

Use auto scan mode

Set scan_mode=auto to get incremental scans on PRs and full scans on pushes to main. Incremental diffs against base_branch with git, so the uploaded archive must include its .git history (the base_branch ref + the commit). If .git is missing, the scan falls back to a full scan. Configure via .scrutora.yml or API params.

Gate deployments on /details

Use the /details endpoint with fail_threshold to get granular pass/fail based on new findings only, not the entire codebase.

Add .scrutora.yml to your repo

Configure frameworks, scan modes, and fail thresholds in your repo. No need to change CI scripts when settings change.

Run full scans on main

Always run full scans when merging to main. This refreshes the baseline for future incremental PR scans.

Exclude non-code files

Zip only source code. Exclude node_modules, venv, build artifacts, and binary files to reduce scan time. Keep .git when you use incremental/PR scans; it's required to compute the diff against base_branch; exclude it only for full scans. Use exclude_paths in .scrutora.yml to skip generated code.

Rotate API keys every 90 days

Create new API keys regularly and revoke old ones. Use separate keys per environment (staging vs production).

Use .scrutoraignore for false positives

Add a .scrutoraignore file to suppress known false positives or accepted risks instead of disabling entire rules. Target specific files and lines.

Export SARIF for code scanning

Download SARIF results and upload to GitHub Code Scanning or Azure DevOps for inline violation annotations in your PRs.

Run a full scan before going incremental

Incremental scanning compares against a baseline. Run a full scan on your main branch first so the scanner has a reference point, otherwise incremental results will be unreliable.

Ready to automate compliance scanning in your pipeline?

Generate your API key, add a .scrutora.yml to your repo, and get compliance scanning on every PR in minutes.