API Endpoints

Complete reference for all ScriptAttest API endpoints with request/response schemas and examples.

Note: All endpoints require Enterprise plan and API key authentication. See API Overview for authentication details.

Sites

Manage sites in your organization.

List Sites

GET /api/sites?limit=50

Returns a list of all sites in your organization.

Query Parameters

Parameter Type Description
limit integer Number of sites to return (default: 50, max: 100)

Response

{
  "sites": [
    {
      "id": "uuid",
      "org_id": "uuid",
      "domain": "example.com",
      "name": "My Site",
      "status": "pending" | "active",
      "report_uri_token": "hex_string",
      "created_at": 1234567890,
      "recent_violations": 5,
      "scan_count": 12
    }
  ]
}

Create Site

POST /api/sites

Add a new site to monitor.

Request Body

{
  "domain": "example.com",
  "name": "My Example Site"  // optional
}

Response (201 Created)

{
  "site": {
    "id": "uuid",
    "domain": "example.com",
    "name": "My Example Site",
    "status": "pending",
    "reportUriToken": "hex_string"
  }
}

Errors

  • 400 - Invalid domain format or missing domain
  • 403 - Site limit reached (upgrade required)
  • 409 - Domain already exists for this organization

Get Site

GET /api/sites/:id

Get detailed information for a specific site, including latest scan status and source counts.

Response

{
  "site": {
    "id": "uuid",
    "domain": "example.com",
    "name": "My Site",
    "status": "active",
    "report_uri_token": "hex_string",
    "hasPolicy": true,
    "created_at": 1234567890,
    "updated_at": 1234567890
  },
  "latestScan": {
    "id": "uuid",
    "status": "completed",
    "violationCount": 0,
    "externalSourceCount": 15,
    "createdAt": 1234567890,
    "completedAt": 1234567900
  },
  "sources": {
    "total": 20,
    "approved": 15,
    "pending": 5
  }
}

Update Site

PATCH /api/sites/:id

Update site settings. All fields are optional.

Request Body

{
  "name": "Updated Site Name",
  "autoApproveFirstParty": true,
  "strictScripts": true,
  "strictStyles": false,
  "emailAlertsEnabled": true
}

Response

{
  "success": true
}

Delete Site

DELETE /api/sites/:id

Delete a site and all associated data (scans, policies, sources, schedules, violations).

Response

{
  "success": true
}

Scans

Trigger and monitor CSP discovery and validation scans.

Trigger Discovery Scan

POST /api/sites/:id/scan/discover

Start a discovery scan to find all third-party resources (scripts, stylesheets, fonts, images, etc.) on all enabled pages.

Response

{
  "batchId": "uuid",
  "status": "queued",
  "message": "Discovery scan started"
}

Notes

  • Scans all enabled pages for the site
  • Results are merged across all pages
  • Use discover-status endpoint to poll for completion

Get Discovery Scan Status

GET /api/sites/:id/scan/discover-status?batchId=:batchId

Check the status of a discovery scan.

Response

{
  "status": "queued" | "running" | "completed" | "failed",
  "progress": {
    "pagesScanned": 5,
    "totalPages": 10,
    "sourcesFound": 25
  },
  "results": {
    "externalSources": 25,
    "violations": 0
  }
}

Trigger Validation Scan

POST /api/sites/:id/scan/validate

Validate your current CSP policy against all enabled pages. Requires Pro+ plan.

Response

{
  "batchId": "uuid",
  "status": "queued",
  "message": "Validation scan started"
}

Errors

  • 403 - Validation scans require Pro+ plan
  • 400 - No policy exists for this site

Get Validation Scan Status

GET /api/sites/:id/scan/validate-status?batchId=:batchId

Check the status of a validation scan.

Response

{
  "status": "queued" | "running" | "completed" | "failed",
  "progress": {
    "pagesScanned": 3,
    "totalPages": 5
  },
  "results": {
    "violations": 2,
    "pagesWithViolations": 1
  }
}

Script Attestation

Run behavioral integrity scans to detect script changes and suspicious activity. Requires Pro+ plan.

Start Attestation

POST /api/sites/:id/attest

Start a script attestation scan to verify script integrity and detect behavioral changes.

Response

{
  "scanId": "uuid",
  "status": "queued",
  "message": "Attestation scan started"
}

Errors

  • 403 - ScriptAttest requires Pro+ plan

Get Attestation Status

GET /api/sites/:id/attest/status?scanId=:scanId

Check the status of an attestation scan.

Response

{
  "status": "queued" | "running" | "completed" | "failed",
  "scanId": "uuid",
  "results": {
    "scriptsAnalyzed": 15,
    "suspiciousActivity": 0,
    "baselineMatches": true
  }
}

Set Baseline

POST /api/sites/:id/attest/set-baseline

Set the current attestation results as the baseline for future comparisons.

Response

{
  "success": true,
  "message": "Baseline set successfully"
}

Policies

Generate and manage Content Security Policies.

Generate Policy

POST /api/sites/:id/policies/generate

Generate a new CSP policy based on approved sources. Uses site's strictScripts and strictStyles settings by default.

Request Body (all fields optional)

{
  "strictScripts": true,        // Override site setting
  "strictStyles": false,         // Override site setting
  "reportOnly": false,           // Generate report-only policy
  "includeReportUri": true,      // Include report-uri directive
  "customPolicy": "..."          // Save a manually edited policy
}

Response

{
  "success": true,
  "policy": {
    "id": "uuid",
    "version": 2,
    "policy": "default-src 'self'; script-src 'self' 'sha256-...'; ...",
    "status": "active" | "testing" | "deployed",
    "reportOnly": false,
    "tier": "baseline" | "hardened" | "app-enforced",
    "tierDescription": "Security tier description",
    "warnings": [
      "Third-party script origins present: cdn.example.com - supply chain risk"
    ],
    "thirdPartyScriptOrigins": ["cdn.example.com"],
    "features": {
      "hashBasedScripts": true,
      "scriptSrcElem": true,
      "unsafeInlineScripts": false,
      "unsafeInlineStyles": false,
      "upgradeInsecureRequests": true
    }
  }
}

Notes

  • Policy is generated from all approved sources
  • If customPolicy is provided, it's saved directly (manual edit mode)
  • Policy version is auto-incremented
  • New policy becomes the site's current_policy_id

Update Policy Status

POST /api/sites/:id/policies/:policyId/status

Change a policy's status (e.g., move to testing or deploy).

Request Body

{
  "status": "active" | "testing" | "deployed"
}

Response

{
  "success": true
}

Notes

  • active - Default state for newly created policies
  • testing - Policy is being tested before deployment
  • deployed - Policy is live (only one policy can be deployed at a time)

Restore Policy

POST /api/sites/:id/policies/restore

Restore a previous policy version by creating a new version from it.

Request Body

{
  "policyId": "uuid"
}

Response

{
  "success": true,
  "message": "Created v3 from v1",
  "newPolicyId": "uuid",
  "newVersion": 3,
  "restoredFromVersion": 1
}

Sources

Manage external sources (scripts, stylesheets, fonts, etc.) discovered during scans.

List Sources

GET /api/sites/:id/sources?status=pending&type=script

List all discovered sources with optional filters.

Query Parameters

Parameter Type Description
status string Filter by status: pending, approved, rejected
type string Filter by type: script, style, font, img, connect, etc.

Response

{
  "sources": [
    {
      "id": "uuid",
      "site_id": "uuid",
      "url": "https://cdn.example.com/script.js",
      "domain": "cdn.example.com",
      "type": "script",
      "status": "pending",
      "content_hash": "sha256-...",
      "first_seen_at": 1234567890,
      "last_seen_at": 1234567890
    }
  ],
  "count": 25
}

Approve Domain

POST /api/sites/:id/sources/approve-domain

Approve all sources from a specific domain (optionally filtered by type).

Request Body

{
  "domain": "cdn.example.com",
  "type": "script"  // optional: approve only specific type
}

Response

{
  "success": true,
  "approvedCount": 5
}

Approve Hash

POST /api/sites/:id/sources/approve-hash

Approve a source by its content hash (for inline scripts/styles).

Request Body

{
  "hash": "sha256-abc123...",
  "type": "script" | "style"
}

Response

{
  "success": true
}

Approve Unsafe

POST /api/sites/:id/sources/approve-unsafe

Approve unsafe-inline or unsafe-eval for a specific directive.

Request Body

{
  "directive": "script-src" | "style-src",
  "value": "unsafe-inline" | "unsafe-eval"
}

Response

{
  "success": true
}

Reject Domain

POST /api/sites/:id/sources/reject-domain

Reject all sources from a specific domain.

Request Body

{
  "domain": "malicious.example.com"
}

Response

{
  "success": true,
  "rejectedCount": 3
}

Approve Source

POST /api/sites/:id/sources/:sourceId/approve

Approve a specific source by ID.

Response

{
  "success": true
}

Reject Source

POST /api/sites/:id/sources/:sourceId/reject

Reject a specific source by ID.

Response

{
  "success": true
}

Pages

Manage pages to scan for a site.

List Pages

GET /api/sites/:id/pages

List all pages configured for a site.

Response

{
  "pages": [
    {
      "id": "uuid",
      "site_id": "uuid",
      "path": "/",
      "label": "Homepage",
      "enabled": 1,
      "created_at": 1234567890,
      "last_scanned_at": 1234567890
    }
  ]
}

Add Page

POST /api/sites/:id/pages

Add a new page to scan.

Request Body

{
  "path": "/about",
  "label": "About Page"  // optional
}

Response (201 Created)

{
  "page": {
    "id": "uuid",
    "site_id": "uuid",
    "path": "/about",
    "label": "About Page",
    "enabled": 1,
    "created_at": 1234567890,
    "last_scanned_at": null
  },
  "needsRevalidation": true,
  "message": "Page added. Run validation to include this page in your CSP policy."
}

Update Page

PATCH /api/sites/:id/pages/:pageId

Update page settings (enable/disable, label, etc.).

Request Body

{
  "enabled": false,
  "label": "Updated Label"
}

Response

{
  "success": true
}

Schedules

Create and manage scheduled scans. Requires Pro+ plan.

List Schedules

GET /api/sites/:id/schedules

List all scheduled scans for a site.

Response

{
  "schedules": [
    {
      "id": "uuid",
      "site_id": "uuid",
      "name": "Daily Validation",
      "type": "csp_validation",
      "frequency": "daily",
      "cron_expression": null,
      "timezone": "UTC",
      "enabled": 1,
      "next_run_at": 1234567890,
      "created_at": 1234567890
    }
  ],
  "count": 2
}

Create Schedule

POST /api/sites/:id/schedules

Create a new scheduled scan.

Request Body

{
  "name": "Daily Validation Scan",
  "type": "csp_validation" | "csp_discovery" | "attestation",
  "frequency": "daily" | "weekly" | "monthly" | "custom",
  "cronExpression": "0 2 * * *",  // required if frequency is "custom"
  "timezone": "UTC",               // optional, defaults to UTC
  "enabled": true,                 // optional, defaults to true
  "options": {}                     // optional scan-specific options
}

Response (201 Created)

{
  "id": "uuid",
  "nextRunAt": 1234567890,
  "enabled": true
}

Errors

  • 403 - Schedules require Pro+ plan
  • 400 - Invalid schedule type, frequency, or missing cronExpression for custom frequency

Update Schedule

PATCH /api/sites/:id/schedules/:scheduleId

Update a scheduled scan (same fields as create).

Delete Schedule

DELETE /api/sites/:id/schedules/:scheduleId

Delete a scheduled scan.

Response

{
  "success": true
}

CSP Violation Reports

Endpoint for browsers to report CSP violations. This is automatically configured when you deploy a policy with a report-uri directive.

Report Violation

POST /api/csp/report/:token

Public endpoint (no authentication required) for browsers to report CSP violations. Protected by domain validation and rate limiting.

Request Body

Browsers send CSP violation reports in the following format:

{
  "csp-report": {
    "document-uri": "https://example.com/page",
    "blocked-uri": "https://evil.com/script.js",
    "violated-directive": "script-src",
    "effective-directive": "script-src",
    "original-policy": "default-src 'self'; script-src 'self'",
    "disposition": "enforce",
    "source-file": "https://example.com/app.js",
    "line-number": 42,
    "column-number": 10,
    "status-code": 200
  }
}

Response

Always returns 204 No Content (silent success) to prevent browser console errors.

Security Features

  • Domain validation (checks document-uri matches site domain)
  • Rate limiting (100 reports/hour per IP)
  • Plan-based quotas (Free: 1000/month, Pro: 50000/month, Enterprise: unlimited)
  • Free plan sites are rejected (report-uri is Pro+ feature)
  • Payload size limit (10KB max)

API Keys

Manage API keys for programmatic access. Enterprise plan only.

List API Keys

GET /api/api-keys

List all API keys for your organization.

Response

{
  "keys": [
    {
      "id": "uuid",
      "name": "Production API Key",
      "prefix": "qs_A1B2C3D4",
      "createdAt": 1234567890000,
      "lastUsedAt": 1234567900000
    }
  ],
  "apiAccessEnabled": true
}

Non-Enterprise Response

{
  "keys": [],
  "apiAccessEnabled": false,
  "requiredPlan": "enterprise",
  "message": "API access is only available on the Enterprise plan"
}

Create API Key

POST /api/api-keys

Create a new API key. The full key is only shown once - store it securely immediately.

Request Body

{
  "name": "Production API Key"
}

Response (201 Created)

{
  "id": "uuid",
  "name": "Production API Key",
  "key": "qs_A1B2C3D4E5F6G7H8I9J0K1L2M3N4O5P6Q7R8S9T0U1V2W3X4Y5Z6",
  "prefix": "qs_A1B2C3D4",
  "createdAt": 1234567890000
}

Important

The full API key is only returned once when created. If you lose it, you must delete and create a new key.

Errors

  • 400 - Name is required
  • 403 - API access requires Enterprise plan

Delete API Key

DELETE /api/api-keys/:id

Delete an API key. This action cannot be undone.

Response

{
  "success": true
}

Example: Complete Workflow

Here's a complete example of using the API to set up CSP for a new site:

# 1. Create a site
curl -X POST https://scriptattest.com/api/sites \
  -H "Authorization: Bearer qs_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{"domain": "example.com", "name": "My Site"}'

# 2. Add pages to scan
curl -X POST https://scriptattest.com/api/sites/SITE_ID/pages \
  -H "Authorization: Bearer qs_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{"path": "/", "label": "Homepage"}'

# 3. Run discovery scan
curl -X POST https://scriptattest.com/api/sites/SITE_ID/scan/discover \
  -H "Authorization: Bearer qs_your_api_key"

# 4. Poll for completion
curl -X GET "https://scriptattest.com/api/sites/SITE_ID/scan/discover-status?batchId=BATCH_ID" \
  -H "Authorization: Bearer qs_your_api_key"

# 5. Approve sources
curl -X POST https://scriptattest.com/api/sites/SITE_ID/sources/approve-domain \
  -H "Authorization: Bearer qs_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{"domain": "cdn.example.com", "type": "script"}'

# 6. Generate policy
curl -X POST https://scriptattest.com/api/sites/SITE_ID/policies/generate \
  -H "Authorization: Bearer qs_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{"strictScripts": true, "strictStyles": false}'

# 7. Deploy the policy from the response to your web server