Get Started with BotID

The set up involves adding:

  • a client side component to run challenges,
  • a server side function to classify the session, and
  • a configuration change to ensure requests are routed through BotID.

BotID works best with Vercel in front. If your app is serving traffic through a reverse proxy, BotID can work in a degraded mode. Please reach out to our support team for assistance in getting BotID to work with a reverse proxy in front.

Before setting up BotID, ensure you have a JavaScript project deployed on Vercel.

  1. Add BotID to your project:

    pnpm i botid
  2. Use the appropriate configuration method for your framework to set up proxy rewrites. This ensures that ad-blockers, third party scripts, and more won't make BotID any less effective.

    next.config.js
    import { withBotId } from 'botid/next/config';
     
    const nextConfig = {
      // Your existing Next.js config
    };
     
    export default withBotId(nextConfig);
    nuxt.config.ts
    export default defineNuxtConfig({
      modules: ['botid/nuxt'],
    });

    For other frameworks, add the following configuration values to your vercel.json:

    When using vercel.json configuration, these rewrites only apply in production. For local development behavior, see the Local Development Behavior section.

    vercel.json
    {
      "rewrites": [
        {
          "source": "/149e9513-01fa-4fb0-aad4-566afd725d1b/2d206a39-8ed7-437e-a3be-862e0f06eea3/a-4-a/c.js",
          "destination": "https://api.vercel.com/bot-protection/v1/challenge"
        },
        {
          "source": "/149e9513-01fa-4fb0-aad4-566afd725d1b/2d206a39-8ed7-437e-a3be-862e0f06eea3/:path*",
          "destination": "https://api.vercel.com/bot-protection/v1/proxy/:path*"
        }
      ],
      "headers": [
        {
          "source": "/149e9513-01fa-4fb0-aad4-566afd725d1b/2d206a39-8ed7-437e-a3be-862e0f06eea3/:path*",
          "headers": [
            {
              "key": "X-Frame-Options",
              "value": "SAMEORIGIN"
            }
          ]
        }
      ]
    }
  3. Choose the appropriate method for your framework:

    • Next.js 15.3+: Use initBotId() in instrumentation.client.ts for optimal performance
    • Other Next.js: Mount the <BotIdClient/> component in your layout head
    • Other frameworks: Call initBotId() during application initialization

    Next.js 15.3+ (Recommended)

    We recommend using initBotId() in instrumentation-client.ts for better performance in Next.js 15.3+. For earlier versions, use the React component approach.

    instrumentation-client.ts
    import { initBotId } from 'botid/client/core';
     
    // Define the paths that need bot protection.
    // These are paths that are routed to by your app.
    // These can be:
    // - API endpoints (e.g., '/api/checkout')
    // - Server actions invoked from a page (e.g., '/dashboard')
    // - Dynamic routes (e.g., '/api/create/*')
     
    initBotId({
      protect: [
        {
          path: '/api/checkout',
          method: 'POST',
        },
        {
          // Wildcards can be used to expand multiple segments
          // /team/*/activate will match
          // /team/a/activate
          // /team/a/b/activate
          // /team/a/b/c/activate
          // ...
          path: '/team/*/activate',
          method: 'POST',
        },
        {
          // Wildcards can also be used at the end for dynamic routes
          path: '/api/user/*',
          method: 'POST',
        },
      ],
    });

    Next.js < 15.3

    app/layout.tsx
    import { BotIdClient } from 'botid/client';
    import { ReactNode } from 'react';
     
    const protectedRoutes = [
      {
        path: '/api/checkout',
        method: 'POST',
      },
    ];
     
    type RootLayoutProps = {
      children: ReactNode;
    };
     
    export default function RootLayout({ children }: RootLayoutProps) {
      return (
        <html lang="en">
          <head>
            <BotIdClient protect={protectedRoutes} />
          </head>
          <body>{children}</body>
        </html>
      );
    }
    plugins/botid.client.ts
    import { initBotId } from 'botid/client/core';
     
    export default defineNuxtPlugin({
      enforce: 'pre',
      setup() {
        initBotId({
          protect: [{ path: '/api/post-data', method: 'POST' }],
        });
      },
    });
    src/hooks.client.ts
    import { initBotId } from 'botid/client/core';
     
    export function init() {
      initBotId({
        protect: [
          {
            path: '/api/post-data',
            method: 'POST',
          },
        ],
      });
    }
  4. Use checkBotId() on the routes configured in the <BotIdClient/> component.

    Important configuration requirements: - Not adding the protected route to <BotIdClient /> will result in checkBotId() failing. The client side component dictates which requests to attach special headers to for classification purposes. - Local development always returns isBot: false unless you configure the developmentOptions option on checkBotId(). Learn more about local development behavior.

    Using API routes

    app/api/sensitive/route.ts
    import { checkBotId } from 'botid/server';
    import { NextRequest, NextResponse } from 'next/server';
     
    export async function POST(request: NextRequest) {
      const verification = await checkBotId();
     
      if (verification.isBot) {
        return NextResponse.json({ error: 'Access denied' }, { status: 403 });
      }
     
      const data = await processUserRequest(request);
     
      return NextResponse.json({ data });
    }
     
    async function processUserRequest(request: NextRequest) {
      // Your business logic here
      const body = await request.json();
      // Process the request...
      return { success: true };
    }

    Using Server Actions

    app/actions/create-user.ts
    'use server';
     
    import { checkBotId } from 'botid/server';
     
    export async function createUser(formData: FormData) {
      const verification = await checkBotId();
     
      if (verification.isBot) {
        throw new Error('Access denied');
      }
     
      const userData = {
        name: formData.get('name') as string,
        email: formData.get('email') as string,
      };
     
      const user = await saveUser(userData);
      return { success: true, user };
    }
     
    async function saveUser(userData: { name: string; email: string }) {
      // Your database logic here
      console.log('Saving user:', userData);
      return { id: '123', ...userData };
    }

    BotID actively runs JavaScript on page sessions and sends headers to the server. If you test with curl or visit a protected route directly, BotID will block you in production. To effectively test, make a fetch request from a page in your application to the protected route.

  5. Only available on Pro or Enterprise plans

    From the Vercel dashboard

    • Select your Project
    • Click the Firewall tab
    • Click Configure
    • Enable Vercel BotID Deep Analysis

Client-side code for the BotID Next.js implementation:

app/checkout/page.tsx
'use client';
 
import { useState } from 'react';
 
export default function CheckoutPage() {
  const [loading, setLoading] = useState(false);
  const [message, setMessage] = useState('');
 
  async function handleCheckout(e: React.FormEvent<HTMLFormElement>) {
    e.preventDefault();
    setLoading(true);
 
    try {
      const formData = new FormData(e.currentTarget);
      const response = await fetch('/api/checkout', {
        method: 'POST',
        body: JSON.stringify({
          product: formData.get('product'),
          quantity: formData.get('quantity'),
        }),
        headers: {
          'Content-Type': 'application/json',
        },
      });
 
      if (!response.ok) {
        throw new Error('Checkout failed');
      }
 
      const data = await response.json();
      setMessage('Checkout successful!');
    } catch (error) {
      setMessage('Checkout failed. Please try again.');
    } finally {
      setLoading(false);
    }
  }
 
  return (
    <form onSubmit={handleCheckout}>
      <input name="product" placeholder="Product ID" required />
      <input name="quantity" type="number" placeholder="Quantity" required />
      <button type="submit" disabled={loading}>
        {loading ? 'Processing...' : 'Checkout'}
      </button>
      {message && <p>{message}</p>}
    </form>
  );
}

Server-side code for the BotID Next.js implementation:

app/api/checkout/route.ts
import { checkBotId } from 'botid/server';
import { NextRequest, NextResponse } from 'next/server';
 
export async function POST(request: NextRequest) {
  // Check if the request is from a bot
  const verification = await checkBotId();
 
  if (verification.isBot) {
    return NextResponse.json(
      { error: 'Bot detected. Access denied.' },
      { status: 403 },
    );
  }
 
  // Process the legitimate checkout request
  const body = await request.json();
 
  // Your checkout logic here
  const order = await processCheckout(body);
 
  return NextResponse.json({
    success: true,
    orderId: order.id,
  });
}
 
async function processCheckout(data: any) {
  // Implement your checkout logic
  return { id: 'order-123' };
}
Last updated on August 14, 2025