Configuration Reference

Complete configuration reference for Babel Shield JS. Start with data attributes for zero-config setup, or use BabelShield.init() for full programmatic control.

Zero-Config Initialization (Data Attributes)

The simplest way to initialize Babel Shield -- add data attributes to the script tag:


<script src="https://cdn.babelshield.ai/v1/babel-shield.js"
        data-api-token="YOUR_API_TOKEN"></script>

Supported Data Attributes

Attribute Required Default Description
data-api-token Yes - Your API token
data-blocked-message No Generic Custom message shown when content is blocked
data-feedback-mode No inline Feedback display mode: inline, modal, or silent
data-feedback-container No - CSS selector for custom feedback container
data-debug No false Enable console logging ("true" to enable)
data-fail-open No true Allow forms if API fails ("false" to disable)
data-cdn-url No Auto-detect Custom CDN URL for loading modules (trusted domains only)
data-api-url No Production API Custom API endpoint URL

Security: data-api-url controls where form data is sent. Only use on pages you fully control.

Security: data-cdn-url loads executable JavaScript modules. Only cdn.babelshield.ai, localhost, 127.0.0.1, and .test TLD domains are accepted. Non-allowlisted domains will fail initialization.

Data Attribute Examples

Basic (minimum required):


<script src="https://cdn.babelshield.ai/v1/babel-shield.js"
        data-api-token="YOUR_API_TOKEN"></script>

Custom blocked message:


<script src="https://cdn.babelshield.ai/v1/babel-shield.js"
        data-api-token="YOUR_API_TOKEN"
        data-blocked-message="Please revise your message and try again."></script>

Modal feedback with debug logging:


<script src="https://cdn.babelshield.ai/v1/babel-shield.js"
        data-api-token="YOUR_API_TOKEN"
        data-feedback-mode="modal"
        data-debug="true"></script>

Silent mode (callbacks only, no UI):


<script src="https://cdn.babelshield.ai/v1/babel-shield.js"
        data-api-token="YOUR_API_TOKEN"
        data-feedback-mode="silent"></script>

Global Config Object

For more control while still using auto-init, define window.BabelShieldConfig before loading the script:


<script>
  window.BabelShieldConfig = {
    apiToken: 'YOUR_API_TOKEN',
    debug: true,
    thresholds: {
      spam: 80,
      profanity: 60
    },
    feedback: {
      mode: 'inline',
      messages: {
        spam: 'This looks like spam.',
        default: 'Content blocked.'
      },
      onBlocked: (result, form) => {
        console.log('Blocked:', result.maxCategory);
      }
    }
  };
</script>
<script src="https://cdn.babelshield.ai/v1/babel-shield.js"></script>

Initialization behavior and priority:

  1. If window.BabelShieldConfig is defined when the script loads, auto-init runs using that config.
  2. Otherwise, if the script tag has data-api-token, auto-init runs using data attributes.
  3. If neither is present, initialize manually with BabelShield.init().
  4. If auto-init has already run (via 1 or 2), a subsequent BabelShield.init() call will fail with already_initialized error and will not override the existing configuration.

These approaches are mutually exclusive. For manual init, omit data-api-token from the script tag and do not define window.BabelShieldConfig.

Auto-Init Error Handling

When auto-init fails (e.g., invalid token, network error), the error is logged to the console. For programmatic error handling:

Listen for errors via CustomEvent:


<script>
  // Add listener BEFORE the Babel Shield script loads
  window.addEventListener('babelshield:autoInitError', function (event) {
    console.error('Babel Shield failed:', event.detail.error);
    // event.detail.config contains the config (apiToken/siteToken redacted)
  });
</script>
<script src="https://cdn.babelshield.ai/v1/babel-shield.js"
        data-api-token="YOUR_API_TOKEN"></script>

Check for errors after page load:

if (window.BabelShieldLastInitError) {
  console.error('Auto-init failed:', window.BabelShieldLastInitError);
}

JavaScript Initialization Options

For full control, omit data-api-token and call BabelShield.init() yourself:

await BabelShield.init({
  // Required (one of the following modes):

  // Direct API Mode (default)
  apiToken: 'your-api-token',

  // OR Proxy Mode
  proxyMode: true,
  backendUrl: 'https://yoursite.com/api/babel-shield',
  siteToken: 'your-site-token',

  // Optional settings
  apiUrl: 'https://api.babelshield.ai/api/v1',
  cdnUrl: null,
  customOrigin: null,
  timeout: 15000,
  failOpen: true,
  debug: false,

  // Form Handler configuration
  forms: 'form',
  excludeForms: 'form[data-babel-shield-ignore]',
  autoLoadFormHandler: true,

  // Category configuration
  categories: null,  // null = all categories

  // Threshold configuration (13 categories + overall)
  thresholds: {
    spam: 70,
    profanity: 50,
    sexual: 75,
    offensive: 75,
    violence: 75,
    hate: 30,
    harassment: 75,
    'self-harm': 75,
    deception: 75,
    garbage: 75,
    crypto: 90,
    illicit: 75,
    injection: 80,
    overall: 75
  },

  // Feedback UI configuration
  feedback: {
    mode: 'inline',
    container: null,
    showDetails: false,
    showSummary: true,
    messages: {
      default: 'Your submission could not be processed at this time. Please revise your content and try again, or contact the site administrator.'
    },
    colors: {
      low: '#ffc107',
      medium: '#fd7e14',
      high: '#dc3545',
      critical: '#721c24'
    },
    onBlocked: null,
    onApproved: null
  },


  // API options passed to every request
  apiOptions: {
    use_pipeline: true,
    continue_on_error: true,
    short_circuit: true,
    short_circuit_threshold: 90
  },

  // Cache configuration
  cache: {
    enabled: true,
    ttl: 1800000,
    maxEntries: 100
  }
});

Configuration Options Reference

Authentication

Option Type Default Description
apiToken string - API token for direct mode (required if not using proxy mode)
proxyMode boolean false Enable proxy mode
backendUrl string - Backend proxy URL (required for proxy mode)
siteToken string - Site token for proxy mode (required for proxy mode)

API Settings

Option Type Default Description
apiUrl string 'https://api.babelshield.ai/api/v1' API base URL
cdnUrl string | null null (auto-detect) CDN URL for loading modules (trusted domains only)
customOrigin string null Custom origin for host-restricted tokens (non-browser only)
timeout number 15000 Request timeout in ms (max: 60000)
failOpen boolean true Allow forms to submit if moderation fails
debug boolean false Enable debug logging

Host-Restricted Tokens and customOrigin

API tokens can be restricted to requests from a specific origin (host). When using such tokens:

  • Browser clients -- the browser automatically sends the Origin header for cross-origin requests. No configuration needed.
  • Non-browser clients (testing, SSR, CLI) -- set customOrigin to your origin URL (e.g., 'https://example.com').
await BabelShield.init({
  apiToken: 'your-host-restricted-token',
  customOrigin: 'https://example.com'  // Must match the token's allowed_host
});

The library adds an X-Origin header to all API requests when customOrigin is set.

Form Handler Settings

Option Type Default Description
forms string 'form' CSS selector for forms to protect
excludeForms string 'form[data-babel-shield-ignore]' CSS selector for forms to exclude
autoLoadFormHandler boolean true Auto-load form handler after init

Form Selection Examples

// Protect all forms (default)
forms: 'form'

// Protect only specific forms
forms: 'form.protected, form[data-babel-shield], #contact-form'

// Protect all forms except login
forms: 'form'
excludeForms: 'form#login, form.auth, [data-babel-shield-ignore]'

Excluding Forms via HTML Attribute

The simplest way to exclude a form is with the data-babel-shield-ignore attribute:

<!-- This form is NOT protected -->
<form data-babel-shield-ignore
      action="/search">
  <input type="search"
         name="q">
  <button type="submit">Search</button>
</form>

Excluding Individual Fields

Exclude specific fields from moderation (content not sent to API):


<form>
  <textarea name="message"></textarea>

  <!-- This field's content won't be moderated -->
  <input type="text"
         name="coupon-code"
         data-babel-shield-ignore>

  <button type="submit">Send</button>
</form>

Disabling Auto-Load

To manually control when the form handler loads:

await BabelShield.init({
  apiToken: 'your-token',
  autoLoadFormHandler: false
});

// Later, manually load and initialize
const formHandler = await BabelShield.loadModule('form-handler');
await formHandler.init();

Framework Adapter Configuration

Babel Shield auto-detects the active framework (WordPress, Drupal, or vanilla HTML) on page load. The detected adapter customizes form selectors, system field removal, and submission handling.

Auto-Detection

Adapters are checked in priority order:

  1. DrupalAdapter -- detects window.Drupal, drupalSettings, etc.
  2. WordPressAdapter -- detects window.wp, wp-content scripts, generator meta, plugin globals
  3. BaseAdapter -- fallback for vanilla HTML forms

The highest-priority adapter whose detect() returns true is activated.

Manual Adapter Selection

Use FrameworkRegistry.setActiveAdapter() to bypass auto-detection or pass custom options:

const formHandler = BabelShield.getModule('form-handler');
const {FrameworkRegistry} = formHandler;

// Pass adapter name with options
FrameworkRegistry.setActiveAdapter('wordpress', {
  includeWPForms: false,
  additionalSelectors: ['.my-custom-form'],
});

Important: Call setActiveAdapter() before FormHandler.init() to ensure your adapter is used from the start. If called after init(), forms already attached will keep using the auto-detected adapter. To control timing precisely, use autoLoadFormHandler: false and initialize manually.

WordPress Adapter Options

Option Type Default Description
includeWPForms boolean true Protect WPForms forms
includeCF7 boolean true Protect Contact Form 7 forms
includeGravityForms boolean true Protect Gravity Forms
includeCommentForms boolean true Protect native comment forms (#commentform)
additionalSelectors string[] [] Additional CSS selectors for forms to protect
additionalExcludes string[] [] Additional CSS selectors for forms to skip
additionalSystemFieldPatterns RegExp[] [] Additional regex patterns for system fields to strip before moderation

WordPress automatically excludes login, registration, password reset, WooCommerce checkout, admin, and search forms.

Adapter Events

When a framework adapter vetoes a submission (e.g., Gravity Forms multi-page navigation), the form handler emits a form:skipped event:

Event Fires When Payload
form:skipped shouldModerateSubmission() returns false { form, adapter, reason: 'adapter_veto' }
BabelShield.on('form:skipped', (data) => {
  console.log('Form skipped by ' + data.adapter + ':', data.reason);
});

Drupal Adapter Options

Option Type Default Description
includeNodeForms boolean true Protect node edit forms
includeCommentForms boolean true Protect comment forms
includeWebforms boolean true Protect Webform module forms
includeContactForms boolean true Protect contact forms
additionalSelectors string[] [] Additional CSS selectors for forms to protect
additionalExcludes string[] [] Additional CSS selectors for forms to skip

Content Moderation

Category Configuration

Option Type Default Description
categories string[] null Categories to check (null = all)
thresholds object See below Score thresholds per category
thresholds.overall number 75 Default threshold for unlisted categories

Default Thresholds

{
  spam: 70,
  profanity: 50,
  sexual: 75,
  offensive: 75,
  violence: 75,
  hate: 30,
  harassment: 75,
  'self-harm': 75,
  deception: 75,
  garbage: 75,
  crypto: 90,
  illicit: 75,
  injection: 80,
  overall: 75
}

Scores are 0-100 integers. Content is flagged when score >= threshold.

Note: If upgrading from an earlier version, update your threshold configuration -- 'hate-speech' is now 'hate', and 'marketing' is now captured under 'spam'. Old category names in your threshold config will be silently ignored and the overall threshold (default 75) will apply instead.

Severity Ranges

Scores map to severity levels:

Severity Score Range
low 0-25
medium 26-50
high 51-75
critical 76-100

Feedback Configuration

Option Type Default Description
feedback.mode string 'inline' Feedback display mode
feedback.container string null CSS selector for custom feedback container
feedback.showDetails boolean false Show category scores in feedback
feedback.showSummary boolean true Show AI summary if available
feedback.messages object See below Custom messages per category
feedback.colors object See below Severity colors (CSS hex values)
feedback.onBlocked function null Callback when content is blocked
feedback.onApproved function null Callback when content is approved

Feedback Modes

  • 'inline' -- shows feedback message below the form (default)
  • 'modal' -- shows feedback in a popup dialog
  • 'silent' -- no UI feedback, callbacks only

Callback Signatures

Callbacks receive the moderation result and the form element:

feedback: {
  onBlocked: (result, form) => {
    // result object (full shape documented in the API Reference):
    // {
    //   flagged: true,
    //   scores: { spam: 85, offensive: 42, profanity: 0, ... },  // access defensively: scores.spam ?? 0
    //   primaryCategory: 'spam',   // server-provided highest-scoring category
    //   primaryScore: 85,
    //   maxCategory: 'spam',       // client-computed highest scorer (can differ from primaryCategory)
    //   maxScore: 85,
    //   maxSeverity: 'critical',
    //   totalScore: null,          // optional API aggregate -- often null; prefer maxScore/primaryScore
    //   summary: 'AI summary...',  // only when the API returns one
    //   categoriesFlagged: ['spam'],
    //   effectiveThresholds: { spam: 70 }
    // }
    // The user-facing display text is built by the library; it is NOT a field on result.

    console.log('Blocked:', result.primaryCategory);
  },

  onApproved: (result, form) => {
    console.log('Approved with score:', result.maxScore);
  }
}

Note: Callbacks fire for ALL modes (inline, modal, silent), not just silent mode.

Custom Feedback Container

By default, inline feedback is inserted after the form element. Use feedback.container to place feedback in a specific element instead:


<div id="form-errors"></div>

<form id="contact">
  <textarea name="message"></textarea>
  <button type="submit">Send</button>
</form>

<script>
  await BabelShield.init({
    apiToken: 'YOUR_API_TOKEN',
    feedback: {
      container: '#form-errors'
    }
  });
</script>

Or via data attribute:


<div id="form-errors"></div>

<script src="https://cdn.babelshield.ai/v1/babel-shield.js"
        data-api-token="YOUR_API_TOKEN"
        data-feedback-container="#form-errors"></script>

If the selector does not match any element, a warning is logged and feedback falls back to the default position (after the form).

Note: The container option only affects inline mode. Modal mode always uses a full-page overlay on document.body.

Default Messages

By default, blocked content shows a generic message that does not reveal the moderation category:

Your submission could not be processed at this time. Please revise your content and try again, or contact the site administrator.

This prevents bad actors from learning what triggers detection. To use category-specific messages, provide overrides via feedback.messages:

await BabelShield.init({
  apiToken: 'YOUR_API_TOKEN',
  feedback: {
    messages: {
      spam: 'This appears to be spam content.',
      profanity: 'Please remove inappropriate language.',
      hate: 'Hate speech is not allowed.',
      offensive: 'Offensive content is not permitted.',
      crypto: 'Cryptocurrency content is not allowed.',
      default: 'Content blocked by moderation system.'
    }
  }
});

Any category not listed falls through to the default message.

CSS Class Reference

All Babel Shield UI elements use the babel-shield- prefix. Override these classes in your stylesheet to customize the appearance:

Class Applied To Description
.babel-shield-feedback <div> Base class for all inline feedback elements
.babel-shield-feedback-low <div> Low severity variant (yellow/amber)
.babel-shield-feedback-medium <div> Medium severity variant (orange)
.babel-shield-feedback-high <div> High severity variant (red)
.babel-shield-feedback-critical <div> Critical severity variant (dark red)
.babel-shield-feedback-icon <span> Warning/error icon within feedback
.babel-shield-feedback-message <span> Message text within feedback
.babel-shield-feedback-details <div> Container for category score badges (when showDetails: true)
.babel-shield-feedback-badge <span> Individual category score badge
.babel-shield-feedback-dismiss <button> Dismiss/close button on feedback
.babel-shield-modal <div> Modal overlay container
.babel-shield-modal-visible <div> Added to modal when visible
.babel-shield-modal-backdrop <div> Semi-transparent backdrop behind modal
.babel-shield-modal-content <div> Modal content card
.babel-shield-modal-header <div> Modal header with icon and title
.babel-shield-modal-icon <span> Icon in modal header
.babel-shield-modal-title <h2> Title in modal header
.babel-shield-modal-message <p> Message paragraph in modal
.babel-shield-modal-summary <p> AI summary paragraph in modal
.babel-shield-modal-details <ul> Category details list in modal
.babel-shield-modal-close <button> Close button in modal

Example custom styles:

/* Make feedback messages full-width with rounded corners */
.babel-shield-feedback {
    border-radius: 8px;
    width: 100%;
    box-sizing: border-box;
}

/* Use brand colors for high severity */
.babel-shield-feedback-high {
    background-color: #fff0f0;
    border-color: #cc0000;
    color: #660000;
}

/* Hide the icon */
.babel-shield-feedback-icon {
    display: none;
}

Severity Colors

colors: {
  low: '#ffc107',      // Yellow/amber
  medium: '#fd7e14',   // Orange
  high: '#dc3545',     // Red
  critical: '#721c24'  // Dark red
}

Colors are injected as CSS custom properties and can be customized to match your corporate identity. The library automatically derives background and text colors from these values using CSS color-mix().

Runtime Color Updates

You can update colors at runtime after initialization:

const formHandler = BabelShield.getModule('form-handler');
const {ValidationUI} = formHandler;

// Update all colors
ValidationUI.setColors({
  low: '#28a745',
  medium: '#17a2b8',
  high: '#dc3545',
  critical: '#6f42c1'
});

// Update a single color (others keep current values)
ValidationUI.setColors({
  critical: '#000000'
});

// Get default colors
const defaults = ValidationUI.getDefaultColors();

CSS Custom Properties

The library creates these CSS custom properties on :root:

:root {
    --babel-shield-color-low: #ffc107;
    --babel-shield-color-medium: #fd7e14;
    --babel-shield-color-high: #dc3545;
    --babel-shield-color-critical: #721c24;
}

You can override these directly in your CSS:

:root {
    --babel-shield-color-low: #your-brand-green;
    --babel-shield-color-critical: #your-brand-red;
}

Webhook Configuration

Option Type Default Description
webhook.enabled boolean false Enable webhook notifications
webhook.url string null Webhook endpoint URL (required when enabled)
webhook.events string[] ['blocked'] Events to send: 'blocked', 'approved', 'error', 'init'
webhook.headers object {} Custom headers to include in webhook requests
webhook.timeout number 5000 Request timeout in ms

API Options

Option Type Default Description
apiOptions.use_pipeline boolean true Use pipeline processing for moderation
apiOptions.continue_on_error boolean true Continue processing if a category handler fails
apiOptions.short_circuit boolean true Stop processing if threshold exceeded early
apiOptions.short_circuit_threshold number 90 Score threshold for short-circuit (0-100)

Cache Configuration

Option Type Default Description
cache.enabled boolean true Enable response caching
cache.ttl number 1800000 Cache TTL in milliseconds (30 min)
cache.maxEntries number 100 Maximum number of cached entries

Proxy Mode Configuration

Backend Endpoint Requirements

Your backend must implement these endpoints:

POST /authorize

Validates the site token and returns authorization status.

Request:

{
  "site_token": "your-site-token",
  "domain": "yoursite.com"
}

Response (success):

{
  "authorized": true,
  "user": {
    "id": 1,
    "name": "Site Name"
  }
}

Response (failure):

{
  "authorized": false,
  "reason": "invalid_token",
  "message": "Invalid site token"
}

POST /moderate

Forwards moderation requests to the Babel Shield API.

Request:

{
  "site_token": "your-site-token",
  "req-id": "uuid",
  "payload": "content",
  "categories": [
    "spam"
  ]
}

Your backend should:

  1. Validate the site token
  2. Look up the real API token
  3. Forward to https://api.babelshield.ai/api/v1/moderate
  4. Add Authorization: Bearer {apiToken} header
  5. Return the response to the client

Example Backend Implementation

Node.js/Express:

const express = require('express');
const app = express();

// Site token to API token mapping
const siteTokens = {
  'site-token-123': 'real-api-token-abc'
};

app.post('/api/babel-shield/authorize', (req, res) => {
  const {site_token, domain} = req.body;

  if (siteTokens[site_token]) {
    res.json({
      authorized: true,
      user: {id: 1, name: domain}
    });
  }
  else {
    res.status(401).json({
      authorized: false,
      reason: 'invalid_token'
    });
  }
});

app.post('/api/babel-shield/moderate', async (req, res) => {
  const {site_token, ...body} = req.body;
  const apiToken = siteTokens[site_token];

  if (!apiToken) {
    return res.status(401).json({error: 'Invalid site token'});
  }

  const response = await fetch('https://api.babelshield.ai/api/v1/moderate', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': 'Bearer ' + apiToken
    },
    body: JSON.stringify(body)
  });

  const data = await response.json();
  res.status(response.status).json(data);
});

Configuration Validation

The configuration is validated on initialization:

const result = await BabelShield.init({
  apiToken: 'token',
  timeout: -1  // Invalid: not a positive number
});

// result:
// {
//   success: false,
//   error: 'config_error',
//   message: 'timeout must be a positive number',
//   field: 'timeout'
// }

Capping vs. error. Only a non-number or a value below 0 returns a config_error (its runtime message reads timeout must be a positive number). A value of 0 passes validation but aborts every request on the next tick, so it is never useful -- always pass a positive value. A timeout over 60000 ms is not an error -- it is capped to 60000 with a console warning and initialization continues. See the Validation Rules table below.

Validation Rules

Field Validation
apiToken Required, non-empty string
apiUrl Valid URL
cdnUrl Valid URL
backendUrl Valid URL (required for proxy mode)
siteToken Required for proxy mode
timeout Number ≥ 0 (but 0 aborts requests immediately -- use a positive value); values over 60000 ms are capped (with a warning) to 60000, not rejected
thresholds.* 0-100 integer
categories Array of strings or null

Runtime Configuration Access

// Get full config (after initialization)
const config = BabelShield.getConfig();

// Config is frozen (immutable)
config.apiToken = 'new';  // Throws error in strict mode, silently fails otherwise

Note: The configuration object is deeply frozen after initialization to prevent accidental modification. Default values are merged with your provided options during init().

Account Status

BabelShield.getAccountStatus({ forceRefresh }) returns a normalized snapshot of your account — validity, usage/quota, account, and token metadata — over the Authorization module's 5-minute cache. It is a diagnostic call, so its error model differs from moderation (which is fail-open):

const status = await BabelShield.getAccountStatus();
// {
//   valid: true,
//   user:    { name, email },
//   usage:   { current, softLimit, hardLimit, remaining },  // remaining = max(0, hardLimit - current)
//   account: { name, company },
//   token:   { name, abilities, lastUsedAt, expiresAt },
// }

// Bypass the cache for a fresh check:
const fresh = await BabelShield.getAccountStatus({ forceRefresh: true });
  • An invalid/suspended account (401/402/403) resolves to { valid: false, reason } — it does not reject (an invalid account is the answer, not an error).
  • A network failure or any other HTTP status (5xx, 429, 400, …) rejects regardless of failOpen — a diagnostic call surfaces transport failures rather than silently swallowing them.
  • In proxy mode usage, account, and token are null (direct-mode-only — the browser must not hold a token able to read account internals); only valid and user are populated.
  • Must be called after init() — it rejects otherwise.

Next Steps