Documentation

API reference

Three endpoints: a fast synchronous scan, an async deep-analysis queue, and a polling endpoint. Responses follow a versioned JSON contract.

X-API-Key (production only)

In production, direct API calls require an X-API-Key header (configured via the comma-separated API_KEYS environment variable); requests without a valid key get 401. Same-origin requests from the hosted form are exempt, and development mode is permissive.

curl -X POST https://prompt-defenders.vercel.app/api/scan \
  -H "Content-Type: application/json" \
  -H "X-API-Key: your-api-key-here" \
  -d '{"input":"test"}'

POST /api/scan

Fast regex scan against the versioned rule pack. Input is HMAC-hashed in memory and never stored; maximum input size is 100KB (400 above it).

{
  "input": "Your text to scan here",
  "deepAnalysis": false
}
{
  "success": true,
  "analysis": {
    "score": 50,
    "severity": "high",
    "categories": ["Direct instruction override attempts"],
    "advisories": [
      {
        "ruleId": "PI-001",
        "description": "Direct instruction override attempts",
        "severity": "high",
        "rationale": "Common pattern in prompt injection attacks..."
      }
    ]
  },
  "meta": {
    "inputHash": "abc123...",
    "inputLength": 45,
    "rulesVersion": "1.0.2",
    "timestamp": "2026-06-09T20:00:00.000Z"
  }
}

With "deepAnalysis": true the response also carries a queue reference:

{
  "success": true,
  "analysis": { "...": "same as above" },
  "meta": { "...": "same as above" },
  "deepAnalysis": {
    "queueId": "uuid-here",
    "status": "pending",
    "pollEndpoint": "/api/scan/result?id=uuid-here"
  }
}
  • Rate limit: 10 requests/minute per IP — 429 when exceeded, with an X-RateLimit-Remaining header on every response. If the rate-limit backend is unavailable, requests fail open to preserve availability.
  • The response shape is governed by the versioned contract at /api/scanner/schema.json, and tests/apiScan.test.ts validates responses against that file in CI.

POST /api/scan/deep

Enqueues async deep analysis for a previously scanned input. Only the input hash and length are submitted — never raw text. Same rate limits as /api/scan.

Honest caveat: the deep-analysis worker currently uses a placeholder LLM stub; real LLM integration is a roadmap item. Treat deep results as illustrative until then.

GET /api/scan/result?id=<queueId>

Polls a queued deep analysis. Jobs expire after 24 hours. Response states:

202 Accepted   { "success": true,  "status": "pending" | "processing", ... }
200 OK         { "success": true,  "status": "completed", "result": { "deepScore": 75, ... } }
200 OK         { "success": false, "status": "failed", "error": "Analysis failed: reason" }
404 Not Found  { "success": false, "error": "Job not found. It may have expired (24 hour TTL)." }