Integration

Server-Side Integration

Configure the SDK to pass session data to your backend

Server-Side Integration

While BotSigged provides client-side detection and automatic form protection, many applications need to verify bot scores on the server before processing sensitive actions like account creation, purchases, or authentication.

This guide covers three opt-in methods to pass the BotSigged session ID to your backend:

  1. Cookies - Automatic first-party cookies for traditional server-rendered apps
  2. Headers - Automatic header injection for SPAs using fetch/XHR
  3. Form Inputs - Hidden form fields for standard HTML form submissions

All three methods are disabled by default to maintain BotSigged’s cookie-free positioning. Enable only what you need.

Quick Start

BotSigged.init({
  apiKey: 'your-api-key',

  // Enable any combination:
  cookie: true,      // Sets _bsid and _bsrisk cookies
  headers: true,     // Adds X-BotSigged-ID header to fetch/XHR
  formInject: true,  // Injects _bsid hidden input into forms
});

Cookie Integration

Best for traditional server-rendered applications (Rails, Django, Laravel, Phoenix) where every request naturally includes cookies.

Configuration

// Enable with defaults
BotSigged.init({
  apiKey: 'your-api-key',
  cookie: true,
});

// Or customize
BotSigged.init({
  apiKey: 'your-api-key',
  cookie: {
    name: '_bsid',           // Session ID cookie name
    riskName: '_bsrisk',     // Risk tier cookie name
    path: '/',               // Cookie path
    sameSite: 'Strict',      // SameSite attribute
    secure: true,            // Secure flag (auto-detected from protocol)
  },
});

Cookies Set

Cookie Value Purpose
_bsid Session GUID Use this to look up full session data via API
_bsrisk human, suspicious, or bot Quick risk tier for WAF/edge decisions

The _bsrisk cookie updates automatically when the user’s risk tier crosses a threshold:

Risk Tier Bot Score Description
human < 40 Low risk, likely human
suspicious 40-69 Medium risk, consider additional verification
bot >= 70 High risk, likely automated

Server-Side Usage

Node.js/Express:

app.post('/api/signup', async (req, res) => {
  const sessionId = req.cookies._bsid;
  const riskTier = req.cookies._bsrisk;

  // Quick decision based on risk tier
  if (riskTier === 'bot') {
    return res.status(403).json({ error: 'Request blocked' });
  }

  // Or get full session data from API
  const session = await botsigged.getSession(sessionId);

  if (session.bot_score > 50) {
    // Require additional verification
  }

  // Process signup...
});

Ruby on Rails:

class SignupsController < ApplicationController
  def create
    session_id = cookies[:_bsid]
    risk_tier = cookies[:_bsrisk]

    if risk_tier == 'bot'
      render json: { error: 'Request blocked' }, status: :forbidden
      return
    end

    # Get full session data
    session = Botsigged.get_session(session_id)

    if session['bot_score'] > 50
      # Require additional verification
    end

    # Process signup...
  end
end

Phoenix/Elixir:

def create(conn, params) do
  session_id = conn.cookies["_bsid"]
  risk_tier = conn.cookies["_bsrisk"]

  case risk_tier do
    "bot" ->
      conn
      |> put_status(:forbidden)
      |> json(%{error: "Request blocked"})

    _ ->
      # Get full session data
      {:ok, session} = Botsigged.get_session(session_id)

      if session.bot_score > 50 do
        # Require additional verification
      end

      # Process signup...
  end
end

WAF Integration

The _bsrisk cookie enables edge-level blocking without API calls:

Cloudflare WAF Rule:

(http.cookie contains "_bsrisk=bot")

Action: Block or Challenge

AWS WAF:

{
  "Name": "BlockBotSiggedBots",
  "Statement": {
    "ByteMatchStatement": {
      "FieldToMatch": { "SingleHeader": { "Name": "cookie" } },
      "SearchString": "_bsrisk=bot",
      "TextTransformations": [{ "Type": "NONE", "Priority": 0 }],
      "PositionalConstraint": "CONTAINS"
    }
  },
  "Action": { "Block": {} }
}

Header Injection

Best for single-page applications (React, Vue, Angular) that make API calls via fetch() or XMLHttpRequest.

Configuration

// Enable with defaults
BotSigged.init({
  apiKey: 'your-api-key',
  headers: true,
});

// Or customize the header name
BotSigged.init({
  apiKey: 'your-api-key',
  headers: {
    name: 'X-BotSigged-ID',  // Default header name
  },
});

How It Works

When enabled, the SDK automatically patches window.fetch and XMLHttpRequest to inject the session ID header into all same-origin requests. Cross-origin requests are not modified to avoid CORS issues.

// Your code - no changes needed
const response = await fetch('/api/checkout', {
  method: 'POST',
  body: JSON.stringify(cart),
});

// BotSigged automatically adds:
// X-BotSigged-ID: <session-guid>

Server-Side Usage

Node.js/Express:

app.post('/api/checkout', async (req, res) => {
  const sessionId = req.headers['x-botsigged-id'];

  if (!sessionId) {
    // No BotSigged session - might be a direct API call
    return res.status(400).json({ error: 'Missing session' });
  }

  const session = await botsigged.getSession(sessionId);

  if (session.bot_score >= 70) {
    return res.status(403).json({ error: 'Request blocked' });
  }

  // Process checkout...
});

Python/FastAPI:

@app.post("/api/checkout")
async def checkout(request: Request):
    session_id = request.headers.get("x-botsigged-id")

    if not session_id:
        raise HTTPException(status_code=400, detail="Missing session")

    session = await botsigged.get_session(session_id)

    if session["bot_score"] >= 70:
        raise HTTPException(status_code=403, detail="Request blocked")

    # Process checkout...

Manual Header Injection

If you prefer not to patch global fetch, you can manually include the header:

const sessionId = botsigged.getSessionId();

const response = await fetch('/api/checkout', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'X-BotSigged-ID': sessionId,
  },
  body: JSON.stringify(cart),
});

Form Input Injection

Best for traditional HTML forms that submit via POST without JavaScript.

Configuration

// Enable with defaults
BotSigged.init({
  apiKey: 'your-api-key',
  formInject: true,
});

// Or customize
BotSigged.init({
  apiKey: 'your-api-key',
  formInject: {
    selector: 'form',     // CSS selector for forms to protect
    inputName: '_bsid',   // Hidden input name
  },
});

How It Works

When a form is submitted, the SDK injects a hidden input with the session ID just before submission:

<!-- Your form -->

<form action="/signup" method="POST">
  <input name="email" type="email">
  <input name="password" type="password">
  <button type="submit">Sign Up</button>

<!-- Automatically injected by BotSigged -->

  <input type="hidden" name="_bsid" value="a1b2c3d4-..." data-bs-injected="true">
</form>

Server-Side Usage

Node.js/Express:

app.post('/signup', async (req, res) => {
  const sessionId = req.body._bsid;
  const { email, password } = req.body;

  const session = await botsigged.getSession(sessionId);

  if (session.bot_score >= 70) {
    return res.status(403).send('Unable to create account');
  }

  // Create account...
});

PHP/Laravel:

public function signup(Request $request) {
    $sessionId = $request->input('_bsid');

    $session = Botsigged::getSession($sessionId);

    if ($session['bot_score'] >= 70) {
        abort(403, 'Unable to create account');
    }

    // Create account...
}

Targeting Specific Forms

Use the selector option to only inject into specific forms:

BotSigged.init({
  apiKey: 'your-api-key',
  formInject: {
    selector: 'form.protected',  // Only forms with class="protected"
    inputName: '_bsid',
  },
});
<!-- Will have hidden input injected -->

<form class="protected" action="/signup" method="POST">
  ...
</form>

<!-- Will NOT have hidden input injected -->

<form action="/search" method="GET">
  ...
</form>

Combining Methods

You can enable multiple methods simultaneously:

BotSigged.init({
  apiKey: 'your-api-key',

  // Traditional pages use cookies
  cookie: true,

  // SPA sections use headers
  headers: true,

  // Legacy forms use hidden inputs
  formInject: {
    selector: 'form.legacy',
  },
});

Manual Session ID Access

Regardless of which integration methods you enable, you can always access the session ID directly:

const sessionId = botsigged.getSessionId();

// Use it however you need
localStorage.setItem('botsigged_session', sessionId);

Security Considerations

Session ID Validation

Always validate session IDs server-side:

app.post('/api/action', async (req, res) => {
  const sessionId = req.headers['x-botsigged-id'];

  // Validate format (UUID v4)
  if (!sessionId || !isValidUUID(sessionId)) {
    return res.status(400).json({ error: 'Invalid session' });
  }

  // Verify session exists and belongs to your site
  const session = await botsigged.getSession(sessionId);

  if (!session) {
    return res.status(404).json({ error: 'Session not found' });
  }

  // Check score and make decision
  // ...
});

Don’t Trust Client-Side Scores

The session ID is the only thing sent to your server. Always fetch the actual score from the BotSigged API - never trust a score sent from the client.

// BAD - don't do this
app.post('/api/action', (req, res) => {
  const clientScore = req.body.botScore;  // Can be spoofed!
  if (clientScore < 50) { /* ... */ }
});

// GOOD - fetch from API
app.post('/api/action', async (req, res) => {
  const sessionId = req.headers['x-botsigged-id'];
  const session = await botsigged.getSession(sessionId);  // Trustworthy
  if (session.bot_score < 50) { /* ... */ }
});

Cookie Security

The SDK sets secure cookie attributes by default:

  • SameSite=Strict - Prevents CSRF
  • Secure - Only sent over HTTPS (auto-detected)
  • Path=/ - Available to all paths

For cross-subdomain use, you may need to adjust:

BotSigged.init({
  apiKey: 'your-api-key',
  cookie: {
    sameSite: 'Lax',  // Allow cross-subdomain
    path: '/',
  },
});

Next Steps