Sometimes, you may want to authenticate users accessing your search or chat service.

This is typically applicable if you’d like to:

  • Restrict answers of the bot to content that a user has access to. Typically applicable for products with private, gated documentation.
  • Provide context (information) about the user or apply source filters in a way that cannot be tampered.
  • Implement your own CAPTCHA, throttling, or anti-bot security measures and use a JWT to verify that the user has passed it.

Generate verification keys

Generate a private and public key pair using openssl or the method of your choice.

openssl genpkey -algorithm RSA -out private_key.pem -pkeyopt rsa_keygen_bits:2048
openssl rsa -pubout -in private_key.pem -out public_key.pem

The private key will be used by your backend service to mint JWT tokens that are sent to the Inkeep search and chat service. It should not be shared with any other service or third party.

Configure the integration

In your Inkeep Integration, configure the following under Advanced Settings:

  • Require user authentication: set to true
  • Public key: provide the value of public_key.pem
  • Issuer: the value we should ensure matches the iss claim in the authentication token. Example: This is just a string URI so can be arbitrary. It just needs to uniquely identify your service.

We will use the public key to validate the token that is provided to our service was created (‘minted’) by your service. We check the signature of the token and the iss, exp, aud and other standard claims.

Generating a token in your backend

You need a Microservice to create a JWT token every time a user is looking to make a search or chat.

Here is an example of how to generate a token in Node.js:

import fs from "fs";
import jwt from "jsonwebtoken";

interface TokenConfiguration {
  iss: string;
  aud: string;
  integrationId: string;
  filters?: { // optional
    attributes: {
      $and: Array<{
        [key: string]: string | { $in: string[] };
  userAttributes?: { // optional
    [key: string]: string | number | string[];
  expirationOffsetHours?: number;

const generateToken = ({
  expirationOffsetHours = 24,
}: TokenConfiguration): string => {
  const privateKey = fs.readFileSync("private_key.pem", "utf8"); // your private key

  const payload = {
    aud: "",
    exp: Math.floor( / 1000) + expirationOffsetHours * 60 * 60,

  const token = jwt.sign(payload, privateKey, { algorithm: "RS256" });

  return token;

const exampleConfiguration: TokenConfiguration = {
  iss: "",
  integrationId: "inkeep-integration-id",
  filters: {
    attributes: {
      $and: [
          env: "dev",
          modules: {
            $in: ["module1", "module2"],
  userAttributes: {
    userId: "123456789",
    userEmail: "",
    userCohorts: [`group1`, `group2`],
  expirationOffsetHours: 24,

const token = generateToken(exampleConfiguration);

The token should include the following claims:

  • iss: The issuer of the token. Example
  • aud: The audience of the token. It should always be
  • integrationId: The ID of the integration. This must match the integration ID in the body of the request.
  • filters (optional): Same as documented in the Start new chat request body. Takes precedence over the filters value provided in the request body.
  • userAttributes (optional): Same as documented in the Start new chat request body. Takes precedence over the userAttributes value provided in the request body.
  • exp: The expiration time of the token, in seconds since the epoch.

Add the token to chat or search requests

When using the Inkeep widgets

Pass in the token you mint to the widget configuration as baseSettings.userToken. See here.

When using the Inkeep API

Make sure to include these two headers:

  • Authorization : Bearer {integrationApiKey} (must still be present in all requests)