dot CMS

How to Integrate Your Headless App with the dotAI APIs

How to Integrate Your Headless App with the dotAI APIs

Share this article on:

dotAI integrates powerful AI capabilities directly into your dotCMS instance, enabling semantic searches, content generation, and more. It leverages OpenAI services and offers various endpoints for different AI functionalities.

In this post, we'll focus on two specific endpoints:

  • /api/v1/ai/search - For a semantic search that returns matching dotCMS content

  • /api/v1/ai/completions - a POST endpoint that returns both relevant dotCMS content (dotCMSResults) and an AI-generated response (openAiResponse) based on your query.

We'll build a travel information search component that lets users search content in two ways: a direct keyword-based semantic search or an enhanced search that generates an AI summary of the relevant content. Both methods draw from your existing dotCMS content, making your content more discoverable and useful in your headless application.

Here you can see a demonstration of the TravelBot component in action, showcasing both search types we'll implement in this tutorial:

80

Prerequisites

Before starting this integration, ensure you have:

  • A dotCMS instance with dotAI enabled

  • Access to dotCMS authentication token

  • A headless application (React/Next.js)

Introduction

Enhancing your headless application with AI capabilities can significantly improve user experience and content discoverability. In this guide, we'll explore how to implement a Travel Bot component that leverages dotCMS AI APIs to provide both conversational (AI Chat) and keyword-based search functionality. This dual-mode search interface demonstrates the power and flexibility of dotAI within your headless sites.

Step-by-Step Guide

1. Understanding the Example Project

Let's examine our official example that demonstrates how to build a headless application using Next.js integrated with dotCMS. This example showcases the best practices for implementing experiments in a real-world headless architecture:

# Clone the dotCMS repository 
git clone https://github.com/dotCMS/core.git 
cd core/examples/nextjs 
# Install dependencies 
npm install 
# Start the development 
server npm run dev

This example provides a foundation for our implementation. In this guide, we'll focus on explaining the key components and concepts for integrating dotAI in your headless application. The complete final implementation can be found in our GitHub repository [Compare], which includes all the components referenced in this tutorial.

2. Component Mapping Setup

A crucial step in integrating components with DotCMS is setting up the component mapping. This connects DotCMS content types to your React components:

// my-page.js - Simplified for clarity
"use client";

// Import components
import TravelBotComponent from "./content-types/TravelBotComponent";
// Other component imports...

import { DotcmsLayout } from "@dotcms/react";
// Other imports...

// Mapping of components to dotCMS content types
const componentsMap = {
  // Other components...
  DotaiHeadless: TravelBotComponent, // Our TravelBot is mapped as a DotaiHeadless ContentType
};

export function MyPage({ pageAsset, nav }) {
  // Implementation details omitted for brevity
  
  return (
    <div>
      <main>
        <DotLayoutComponent
          pageContext={{
            pageAsset,
            components: componentsMap, // This is where our component map is used
          }}
          // Other config options...
        />
      </main>
    </div>
  );
}

This mapping is essential because it tells the DotCMS layout engine which React component should render each content type. In our case, we've mapped our TravelBotComponent to be used as a DotaiHeadless content type in DotCMS.

Note on Configuration

The component mapping requires a matching ContentType in dotCMS. We've created a video demonstration showing how to set up the "DotaiHeadless" ContentType and parameterize your component. This allows you to configure aspects of your component directly from dotCMS without hardcoding values.

80

3.  Creating the Component Structure

Our implementation follows a clean, organized structure:

  1. TravelBotComponent.js - The main container component

  2. SearchInput.js - Handles user input

  3. SearchTypeSelector.js - Lets users switch between AI Chat and Keyword search modes

  4. SearchResults.js - Displays search results

Let's start by setting up the folder structure:

app/
components/
  content-types/
    travelBotComponent.js  # Main component
  travel-bot/              # Supporting components
    SearchInput.js
    SearchTypeSelector.js
    SearchResults.js

This structure separates the main component (travelBotComponent.js) from its supporting components in the travel-bot folder. This organization makes the code easier to maintain and understand.

4. Implementing the Main Component

The travelBotComponent.js serves as the core of our application, managing state and API calls. Let's focus on the most important parts:

import { useState } from "react";
import SearchInput from "../travel-bot/SearchInput";
import SearchTypeSelector from "../travel-bot/SearchTypeSelector";
import SearchResults from "../travel-bot/SearchResults";

/**
 * TravelBotComponent - A search interface that combines AI-powered and keyword search
 * for travel-related content.
 */
export default function TravelBotComponent({
  indexName,
  placeholderAiSearch,
  placeholderInputSearch,
}) {
  const [searchQuery, setSearchQuery] = useState("");
  const [searchResults, setSearchResults] = useState([]);
  const [isLoading, setIsLoading] = useState(false);
  const [searchType, setSearchType] = useState("aiChat");
  const [answer, setAnswer] = useState("");

  /**
   * Makes requests to DotCMS AI endpoints
   */
  const makeDotCMSAIRequest = async (endpoint, customConfig = {}) => {
    // Base configuration common to both endpoints
    const baseConfig = {
      prompt: searchQuery,      // Search text
      threshold: 0.25,          // Relevance threshold (0-1)
      responseLengthTokens: 500, // Maximum response length
      indexName: indexName,    // DotCMS search index from props
    };

    const response = await fetch(
      `${process.env.NEXT_PUBLIC_DOTCMS_HOST}${endpoint}`,
      {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
          Authorization: `Bearer ${process.env.NEXT_PUBLIC_DOTCMS_AUTH_TOKEN}`,
        },
        body: JSON.stringify({ ...baseConfig, ...customConfig }),
      }
    );
    return response;
  };

  const handleSearch = async () => {
    if (!searchQuery.trim()) return;

    setIsLoading(true);
    setAnswer("");
    setSearchResults([]);

    try {
      if (searchType === "keyword") {
        // Use the search endpoint for keyword search
        const response = await makeDotCMSAIRequest("api/v1/ai/search");
        const data = await response.json();
        setSearchResults(data.dotCMSResults || []);
      } else {
        // Use the completions endpoint for AI-enhanced search
        const response = await makeDotCMSAIRequest("api/v1/ai/completions");
        const data = await response.json();
        setSearchResults(data.dotCMSResults || []);

        if (data.openAiResponse?.choices?.[0]?.message?.content) {
          setAnswer(data.openAiResponse.choices[0].message.content);
        }
      }
    } catch (error) {
      console.error("Error searching:", error);
    } finally {
      setIsLoading(false);
    }
  };

  // ... other utility functions omitted for clarity

  return (
    <div className="max-w-3xl mx-auto p-6">
      <div className="min-h-[200px] flex flex-col">
        <SearchInput
          placeholderAiSearch={placeholderAiSearch}
          placeholderInputSearch={placeholderInputSearch}
          searchQuery={searchQuery}
          setSearchQuery={setSearchQuery}
          searchType={searchType}
          handleSearch={handleSearch}
          isLoading={isLoading}
        />

        <SearchTypeSelector
          searchType={searchType}
          setSearchType={setSearchType}
        />

        {/* UI rendering logic omitted for brevity */}
        {/* Displays loading indicator, answer summary, and search results */}
      </div>
    </div>
  );
}

5. Creating the Search Input Component

The SearchInput.js component handles user input and triggers the search:

export default function SearchInput({
  searchQuery,
  setSearchQuery,
  searchType,
  handleSearch,
  isLoading,
  placeholderAiSearch,
  placeholderInputSearch,
}) {
  const handleKeyDown = (e) => {
    if (e.key === "Enter") {
      handleSearch();
    }
  };

  return (
    <div className="flex gap-3">
      <input
        type="text"
        value={searchQuery}
        onChange={(e) => setSearchQuery(e.target.value)}
        onKeyDown={handleKeyDown}
        placeholder={
          searchType === "aiChat" ? placeholderAiSearch : placeholderInputSearch
        }
        className="flex-1 p-3 border border-gray-300 rounded-lg focus:border-blue-500 focus:ring-1 focus:ring-blue-200 outline-none transition-all"
      />
      <button
        onClick={handleSearch}
        disabled={isLoading}
        className="px-6 py-2 bg-blue-500 text-white font-medium rounded-lg hover:bg-blue-600 transition-colors disabled:bg-blue-400 disabled:cursor-not-allowed"
      >
        {searchType === "aiChat" ? "Ask AI" : "Search"}
      </button>
    </div>
  );
}

6. Implementing the Search Type Selector

The SearchTypeSelector.js component allows users to switch between search modes:

export default function SearchTypeSelector({ searchType, setSearchType }) {
  return (
    <div className="mt-4 flex items-center space-x-6">
      <span className="text-gray-600">Type:</span>
      <label className="flex items-center cursor-pointer">
        <input
          type="radio"
          name="searchType"
          value="aiChat"
          checked={searchType === "aiChat"}
          onChange={(e) => setSearchType(e.target.value)}
          className="w-4 h-4 text-blue-500 border-gray-300 focus:ring-blue-400"
        />
        <span className="ml-2 text-gray-600">AI Chat</span>
      </label>
      <label className="flex items-center cursor-pointer">
        <input
          type="radio"
          name="searchType"
          value="keyword"
          checked={searchType === "keyword"}
          onChange={(e) => setSearchType(e.target.value)}
          className="w-4 h-4 text-blue-500 border-gray-300 focus:ring-blue-400"
        />
        <span className="ml-2 text-gray-600">Keyword</span>
      </label>
    </div>
  );
}

7. Creating the Results Component

The SearchResults.js component displays the search results:

export default function SearchResults({ results }) {
  return (
    <div className="mt-8">
      <h2 className="text-lg font-semibold text-gray-800 mb-4">References</h2>
      <div className="space-y-4">
        {results.map((result, index) => (
          <div
            key={index}
            className="border border-gray-200 rounded-lg p-4 bg-white"
          >
            <a
              href={result.urlMap}
              className="text-orange-500 font-bold hover:underline"
            >
              {result.title}
            </a>
            <p className="text-gray-600 mt-2">{result.teaser}</p>
            <div className="mt-3 flex gap-2">
              <span className="px-2 py-1 bg-gray-100 text-gray-600 rounded text-sm">
                {result.contentType}
              </span>
              {result.matches?.map((match, i) => (
                <span
                  key={i}
                  className="px-2 py-1 bg-blue-50 text-blue-600 rounded text-sm"
                >
                  Score: {Math.round((1 - match.distance) * 100)}%
                </span>
              ))}
            </div>
          </div>
        ))}
      </div>
    </div>
  );
}

8. Configuring dotAI in dotCMS

Before your Travel Bot can work properly, you need to configure dotAI in your dotCMS instance:

  1. Enable dotAI:

    • Go to Apps in your dotCMS admin panel

    • Find dotAI and make sure it's activated

  2. Create an AI Index:

    • Navigate to Dev Tools → dotAI → Manage Embeddings/Indexes

    • Create a new index named "travelBot" (matching the indexName in your code)

    • Configure which content types should be included in this index (e.g., "+contentType:blog")

    • Click "Build Index" to generate the index

  3. Generate an Authentication Token:

    • Go to Settings → Users →  {User} → API Access Tokens

      • Be sure the AI user account is appropriately (i.e., minimally) permissioned to accomplish its duties.

    • Create a new token with the appropriate permissions for AI endpoints

    • Use this token in your environment variables

How It Works Behind the Scenes

Understanding the Two dotAI Endpoints

Our application uses two different dotAI endpoints, each serving a specific purpose:

1. /api/v1/ai/search (Keyword Search)

The first endpoint provides traditional semantic search capabilities. It processes the user's query as a set of keywords and returns relevant content from your dotCMS instance.

Key features:

  • Fast, direct content retrieval

  • Ranks results by relevance

  • Returns matching content items with metadata

  • No conversational response generation

Example request to this endpoint:

{
  "prompt": "beach resorts Mediterranean",
  "threshold": 0.25,
  "responseLengthTokens": 500,
  "indexName": "travelBot"
}

This endpoint is ideal for scenarios where users know exactly what they're looking for and want to browse multiple matching content items.

2. /api/v1/ai/completions (AI Chat)

The second endpoint provides a conversational AI experience. It not only searches for relevant content but also generates a natural language response based on the matched content.

Key features:

  • Conversational approach to content discovery

  • Processes natural language queries

  • Returns relevant content AND an AI-generated response

  • Provides summaries and answers in conversational format

Example request to this endpoint:

{
  "prompt": "What are the best beach destinations in Europe for families?",
  "threshold": 0.25,
  "responseLengthTokens": 500,
  "indexName": "travelBot"
}

This endpoint is perfect for users who prefer a conversational interaction and may have complex or open-ended questions.

The Data Flow

The process flow for either search type follows these steps:

  1. User enters a query and selects search type (AI Chat or Keyword)

  2. Application sends a request to the appropriate dotAI endpoint

  3. dotCMS processes the request:

    • Analyzes the query

    • Searches the specified index ("travelBot")

    • Finds matching content based on relevance

  4. dotCMS returns results to the application

  5. Application displays the results:

    • For keyword search: Just the content results

    • For AI chat: Content results plus an AI-generated summary

The key difference between the two modes is that the completions endpoint (AI Chat) leverages large language models to generate a conversational response in addition to finding relevant content.

Conclusion

Integrating dotAI into your headless application transforms how users discover and interact with your content. By implementing both the keyword search and AI chat functionalities, you provide a flexible, powerful content discovery experience that meets diverse user needs.

The React components we've built demonstrate how easily these capabilities can be integrated into a modern frontend application, while keeping the implementation clean and maintainable.

As AI technology continues to evolve, dotCMS's dotAI will likely gain even more capabilities, which you can incorporate into your application with minimal changes to your existing integration. This future-proofs your investment in AI-powered content discovery.

Additional Resources

By following this guide, you've taken a significant step toward enhancing your headless application with powerful AI capabilities, making your content more accessible and valuable to your users.

Recommended Reading
  • Migrating Your OSGi Plugins to dotEvergreen: Adapting to the New Index API
    24 Mar 26
    Technical Guides

    Migrating Your OSGi Plugins to dotEvergreen: Adapting to the New Index API

    An update on infrastructural changes, information on a breaking change introduced that may affect some plugins, and a migration guide for those affected.

    Fabrizzio

    Fabrizzio Araya

    Staff Software Engineer

  • What Is Rich Text? How It Works in a Headless CMS
    23 Mar 26
    Content Management

    What Is Rich Text? How It Works in a Headless CMS

    What is rich text, and how does it differ from Rich Text Format (.rtf)? Learn how rich text works in content management systems, how headless CMS platforms store it as structured data, and why the format matters for omnichannel delivery.

    Fatima

    Fatima Nasir Tareen

    Growth Marketing Specialist

  • Structured Content for GEO: How dotCMS Powers AI-Ready Digital Experiences
    21 Mar 26
    AI in CMS

    Structured Content for GEO: How dotCMS Powers AI-Ready Digital Experiences

    Discover how dotCMS revolutionizes AI-driven digital experiences with structured content for Generative Engine Optimization (GEO). Learn how our enterprise solution enhances AI visibility, enabling large language models to accurately process and cite machine-readable data. Dive into best practices for creating AI-ready content and explore the benefits of a headless CMS model. Optimize your content for AI discovery and experience seamless omnichannel delivery. Contact us to leverage dotCMS for your AI-powered search needs.

    Fatima

    Fatima Nasir Tareen

    Growth Marketing Specialist

  • AI Content Governance for Content Teams: A Practical Framework
    9 Mar 26
    AI in CMS

    AI Content Governance for Content Teams: A Practical Framework

    Learn why AI content governance is essential for content teams. Discover how to protect brand consistency, reduce legal risk, and manage AI across dozens of sites with dotCMS’s built-in governance tools.

    Fatima

    Fatima Nasir Tareen

    Growth Marketing Specialist

Explore dotCMS for your organization

image

dotCMS Named a Major Player

In the IDC MarketScape: Worldwide AI-Enabled Headless CMS 2025 Vendor Assessment

image

Explore an interactive tour

See how dotCMS empowers technical and content teams at compliance-led organizations.

image

Schedule a custom demo

Schedule a custom demo with one of our experts and discover the capabilities of dotCMS for your business.