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:
- If
window.BabelShieldConfigis defined when the script loads, auto-init runs using that config. - Otherwise, if the script tag has
data-api-token, auto-init runs using data attributes. - If neither is present, initialize manually with
BabelShield.init(). - If auto-init has already run (via 1 or 2), a subsequent
BabelShield.init()call will fail withalready_initializederror 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
Originheader for cross-origin requests. No configuration needed. - Non-browser clients (testing, SSR, CLI) -- set
customOriginto 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:
- DrupalAdapter -- detects
window.Drupal, drupalSettings, etc. - WordPressAdapter -- detects
window.wp, wp-content scripts, generator meta, plugin globals - 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:
- Validate the site token
- Look up the real API token
- Forward to
https://api.babelshield.ai/api/v1/moderate - Add
Authorization: Bearer {apiToken}header - 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, andtokenarenull(direct-mode-only — the browser must not hold a token able to read account internals); onlyvalidanduserare populated. - Must be called after
init()— it rejects otherwise.
Next Steps
- Quick Start -- Zero-config integration with data attributes only
- Vanilla HTML Configuration -- Platform-specific examples and event listeners
- WordPress Configuration -- WordPress plugin-specific settings
- Drupal Configuration -- Drupal module-specific settings
- ESM Import & Programmatic Use -- Full programmatic API
- Troubleshooting -- Common issues and diagnostics
- API Reference -- Full endpoint documentation and response structure