Centinel AnalyticaCentinel Analytica

Validation

When a request hits a protected endpoint, call the validation API to get a decision on whether to allow it.

Overview

For each request to a protected endpoint, your backend should call the validation API and act on the decision:

  • allow: let the request through
  • block: deny the request
  • redirect: return an interstitial challenge page to the browser
  • not_matched: the URL didn't match a protected endpoint (treat as allow)

The validation request should include:

  • request URL + method
  • client IP
  • request headers (especially User-Agent)
  • _centinel cookie (set by the browser script), if present

If you haven't added the browser script yet, start here:

Validate a request

Endpoint

  • Method: POST
  • URL: https://validator.centinelanalytica.com/validate

Headers

  • content-type (string): application/json
  • x-api-key (string): Your secret API key (server-only).

Body

  • url (string): The full URL the user requested.
  • method (string): The request method (GET, POST, etc.).
  • ip (string): The client IP address.
  • referrer (string): The referer header.
  • headers (object): The request headers.
  • cookie (string): The _centinel cookie value (used for session tracking across interstitial and continuous protection).

Tips

  • IP: if you're behind a CDN/reverse proxy, pass the real client IP (not the proxy IP).
  • Headers: include User-Agent (and any other headers you rely on for request attribution).

Keep x-api-key server-only

Don't call /validate from the browser. The x-api-key is your secret key and must only be used server-side.

Request

curl -X POST 'https://validator.centinelanalytica.com/validate' \
  -H 'content-type: application/json' \
  -H 'x-api-key: API_KEY' \
  -d '{"url": "https://example.com", "method": "GET", "ip": "127.0.0.1", "cookie": "1234567890", "referrer": "https://example.com", "headers": { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36" }}'

Response

{
  "success": true,
  "status_code": 200,
  "request_id": "string",
  "decision": "allow" | "block" | "redirect" | "not_matched",
  "response_html": "string",
  "cookies": [{ "name": "string", "value": "string", "path": "string", "domain": "string" }],
  "crawler": {
    "id": "string",
    "name": "string",
    "access_allowed": true,
    "category": "string",
    "rsl_category": "string"
  }
}

Response fields:

  • status_code (integer): The HTTP status code your middleware should use when responding to the client. This can be any integer value (e.g., 200, 403, 302).
  • request_id (string): Unique identifier for this validation request, useful for debugging and support.
  • response_html and cookies only appear with a redirect decision. response_html contains base64-encoded HTML for the interstitial page.
  • crawler is optional—it only appears when the request is detected as a crawler and crawler metadata is enabled for your tenant.

crawler fields

FieldTypeRequiredDescription
idstringYesCrawler identifier (UUID).
namestringYesCrawler name (e.g. Googlebot).
access_allowedbooleanYesWhether this crawler was allowed (true for whitelisted, false otherwise).
categorystringNoHuman-readable category (e.g. Search Engine, AI Assistant). See Crawler categories.
rsl_categorystringYesMachine-readable category key (e.g. search, ai-input). See Crawler categories.

Example (crawler detected)

{
  "success": true,
  "status_code": 200,
  "request_id": "req_abc123xyz789",
  "decision": "allow",
  "crawler": {
    "id": "4a7d7c8a-3fcb-4b91-8f7e-3f6ac1f5b6c1",
    "name": "Googlebot",
    "access_allowed": true,
    "category": "Search Engine",
    "rsl_category": "search"
  }
}

Crawler categories

When crawler is present, crawler.category (human-readable) and crawler.rsl_category (machine-readable) can be:

Category (human-readable)rsl_category (machine-readable)
AI Agentai-all
AI Assistantai-input
AI Searchai-input
AI Trainingai-train
Search Enginesearch
Accessibilityall
Advertisingall
Aggregatorall
Archiverall
Feed Readerall
Monitoringall
Otherall
Previewall
Researchall
Scraperall
Securityall
SEO Toolall
Social Mediaall

Handling decisions

Use the decision field to decide how to respond.

Call /validate from your backend with request metadata (url, method, ip, headers, _centinel cookie).

Read decision and act on it (allow, block, redirect, not_matched).

If decision is redirect, set returned cookies and return the decoded HTML.

type ValidateDecision = 'allow' | 'block' | 'redirect' | 'not_matched';

function enforceDecision(decision: ValidateDecision) {
  switch (decision) {
    case 'allow':
    case 'not_matched':
      // Let the request through
      return;
    case 'block':
      // Return HTTP 403 (or your preferred deny response)
      throw new Error('Blocked by Centinel Analytica');
    case 'redirect':
      // Return the interstitial HTML to the browser (see below)
      return;
  }
}

Handling redirect

When you get a redirect decision:

  • Set cookies from the cookies array on your HTTP response.
  • Base64-decode response_html (then UTF-8 decode) and return it as the response body.

Example in Node.js:

const html = Buffer.from(response.response_html, 'base64').toString('utf8');

Error response

{
  "success": false,
  "status_code": 400,
  "request_id": "string",
  "message": "string"
}

You'll only get an error if the validation request was malformed. Check the message for details.

Decisions explained

Allow

The request passed validation. Let it through.

Not matched

The request URL didn't match any protected endpoint. Let it through.

Block

The request failed validation. Block it.

Redirect

Show the interstitial challenge page. This happens when additional verification is needed.

See Handling redirect for decoding and cookie handling.