Skip to Content
Query API

Query API

The Nexus Platform provides a public query API that enables you to submit questions and receive AI-generated answers from your configured experiences. This endpoint is the core of the platform’s conversational AI capabilities and powers the chat widget.

Overview

The query API accepts natural language questions and returns AI-generated responses based on your experience’s knowledge base, tools, and configuration. Key features include:

  • Optional authentication - Experiences can require access tokens or remain fully public
  • Multi-turn conversations - Track conversations across multiple queries using session IDs
  • Flexible user identification - Support for email addresses, UUIDs, or custom identifiers
  • Origin validation - Optional allowlist for controlling access by domain
  • Custom metadata - Attach tracking data to queries for analytics

Endpoint Details

URL: POST /api/v2/query

Authentication: Optional - depends on experience configuration

Content-Type: application/json

CORS: Fully supported with wildcard origin (*)

Rate Limiting: Standard platform rate limits apply per origin

Authentication

Some experiences may require authentication for query requests.

Providing an access token:

Include an Authorization header with a Bearer token:

fetch('https://nexus-api.uat.knowbl.com/api/v2/query', {
method: 'POST',
headers: {
  'Content-Type': 'application/json',
  'Authorization': 'Bearer YOUR_ACCESS_TOKEN'
},
body: JSON.stringify({
  text: 'What are your hours?',
  experienceId: 'YOUR_EXPERIENCE_ID'
})
});

Obtaining access tokens:

  • Access tokens are obtained via POST /v2/auth/access-tokens
  • Tokens must have the query:execute scope
  • See the Authentication Guide for details on token creation and management

Request Format

Submit queries as JSON with the following fields:

Required Fields

text (string)

The query text - the user’s question or message.

{
  "text": "What are your business hours?"
}

experienceId (string, UUID)

The UUID of the experience to query. This determines which knowledge base, tools, and configuration will be used to answer the query.

{
  "experienceId": "550e8400-e29b-41d4-a716-446655440000"
}

Optional Fields

userId (string, optional)

⚠️ Note: This field may be removed in a future version. For production applications, consider using the Sessions API to create sessions with userId before submitting queries.

User identifier for tracking and personalization. Supports multiple formats:

  • Email address: customer@example.com (case-insensitive)
  • UUID: a1b2c3d4-e5f6-7890-abcd-ef1234567890
  • Custom string: Any string up to 255 characters (e.g., user-12345)

Normalization: All userId values are automatically converted to lowercase for consistency.

Constraints:

  • Minimum 1 character
  • Maximum 255 characters
  • Cannot contain whitespace or control characters
  • Cannot be an empty string (use null or omit for anonymous queries)
{
  "userId": "customer@example.com"
}

sessionId (string, UUID, optional)

Session identifier for multi-turn conversations. Include the sessionId from a previous query response to maintain conversation context.

Important:

  • If provided, must be a valid UUID
  • System validates that the userId matches the session’s original userId
  • Automatically created if not provided - returned in response for use in subsequent queries
{
  "sessionId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}

timestamp (string, ISO 8601, optional)

Query timestamp. Defaults to the current time if not provided.

{
  "timestamp": "2025-10-28T10:30:00.000Z"
}

metadata (object, optional)

⚠️ Note: This field may be removed in a future version. For production applications, consider using the Sessions API to create sessions with metadata before submitting queries, or update session metadata during the conversation.

Custom key-value pairs for tracking and analytics. Use this to attach contextual information about the query source, user environment, or application state.

Common use cases:

  • Source tracking: { "source": "mobile-app" }
  • Device information: { "deviceType": "iOS", "appVersion": "2.1.0" }
  • Page context: { "pagePath": "/account/orders" }
  • Campaign tracking: { "campaign": "summer-sale-2025" }
{
  "metadata": {
    "source": "mobile-app",
    "deviceType": "iOS",
    "appVersion": "2.1.0",
    "pagePath": "/account/orders"
  }
}

Response Format

The API returns a minimal response optimized for widget consumption:

Response Fields

queryId (string, UUID)

Unique identifier for this query. Use this for debugging, support tickets, or analytics tracking.

{
  "queryId": "01234567-89ab-cdef-0123-456789abcdef"
}

answer (string)

The AI-generated answer to the query. This is the primary response content to display to the user.

{
  "answer": "Our business hours are Monday through Friday, 9 AM to 5 PM EST."
}

sessionId (string, UUID)

Session identifier for this conversation. Always returned, even for first queries. Store this value and include it in subsequent queries to maintain conversation context.

{
  "sessionId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}

timestamp (string, ISO 8601)

Response timestamp indicating when the query was processed.

{
  "timestamp": "2025-10-28T10:30:15.234Z"
}

format (string, optional)

Answer format indicator. Reserved for future use to support different content types.

Possible values (planned):

  • "markdown" - Markdown-formatted text
  • "text" - Plain text
  • "html" - HTML content

Currently, all responses are in plain text/markdown format.

{
  "format": "markdown"
}

actions (array, optional)

Array of action buttons to display alongside the answer. Actions enable rich interactions like triggering handovers, opening URLs, or sending follow-up queries.

Action structure:

  • id (string) - Unique action identifier
  • label (string) - Button text
  • icon (object, optional) - Icon configuration
    • type (string) - Icon type: "svg", "url", or "emoji"
    • content (string) - Icon content (SVG markup, URL, or emoji character)
  • action (object) - Action to perform
    • type (string) - Action type: "trigger_handover", "open_url", "send_event", or "send_query"
    • Additional type-specific fields
  • style (object, optional) - Custom styling
  • behavior (object, optional) - Behavior configuration
  • visibility (object, optional) - Visibility rules

Example:

{
  "actions": [
    {
      "id": "talk-to-agent",
      "label": "Talk to an Agent",
      "icon": {
        "type": "emoji",
        "content": "💬"
      },
      "action": {
        "type": "trigger_handover",
        "agentType": "support"
      }
    }
  ]
}

Code Examples

Anonymous Query

Submit a query without user identification:

// Anonymous query without user identification
const response = await fetch("https://nexus-api.uat.knowbl.com/api/v2/query", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    text: "Tell me about your services",
    experienceId: "your-experience-id",
  }),
});

const data = await response.json();
console.log(data);
// {
//   queryId: "01234567-89ab-cdef-0123-456789abcdef",
//   answer: "We offer...",
//   sessionId: "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
//   timestamp: "2025-10-28T10:30:00.000Z"
// }

Query with User ID

Track queries for a specific user using email or custom identifier:

// Query with user identification (email format)
const response = await fetch("https://nexus-api.uat.knowbl.com/api/v2/query", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    text: "What are your business hours?",
    experienceId: "your-experience-id",
    userId: "customer@example.com",
  }),
});

const data = await response.json();
console.log(data);

Query with Session Continuity

Maintain conversation context across multiple queries:

// Query with session continuity for multi-turn conversation
const response = await fetch("https://nexus-api.uat.knowbl.com/api/v2/query", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    text: "What about refund policies?",
    experienceId: "your-experience-id",
    userId: "customer@example.com",
    sessionId: "a1b2c3d4-e5f6-7890-abcd-ef1234567890", // From previous query
  }),
});

const data = await response.json();

// Store sessionId for future queries
if (data.sessionId) {
  localStorage.setItem("nexus-session-id", data.sessionId);
}

Query with Metadata

Include custom metadata for tracking and analytics:

// Query with custom metadata for tracking
const response = await fetch("https://nexus-api.uat.knowbl.com/api/v2/query", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    text: "What's the status of my order?",
    experienceId: "your-experience-id",
    userId: "user@company.com",
    sessionId: "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    metadata: {
      source: "mobile-app",
      deviceType: "iOS",
      appVersion: "2.1.0",
      pagePath: "/account/orders",
    },
  }),
});

const data = await response.json();

Authenticated Query

Submit a query with access token authentication (required for experiences that require authentication):

// Query with access token authentication
const response = await fetch("https://nexus-api.uat.knowbl.com/api/v2/query", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    Authorization: "Bearer YOUR_ACCESS_TOKEN", // Include access token
  },
  body: JSON.stringify({
    text: "What are your business hours?",
    experienceId: "YOUR_EXPERIENCE_ID",
  }),
});

const data = await response.json();

// Store session ID for subsequent queries
const sessionId = data.sessionId;
console.log("Answer:", data.answer);
console.log("Session ID:", sessionId);

cURL Examples

Test the API directly from the command line:

Anonymous Query

# Anonymous query
curl -X POST "https://nexus-api.uat.knowbl.com/api/v2/query" \
  -H "Content-Type: application/json" \
  -H "Origin: https://example.com" \
  -d '{
    "text": "Tell me about your services",
    "experienceId": "your-experience-id"
  }'

Complete Query with All Fields

# Complete query with all fields
curl -X POST "https://nexus-api.uat.knowbl.com/api/v2/query" \
  -H "Content-Type: application/json" \
  -H "Origin: https://example.com" \
  -d '{
    "text": "What are your business hours?",
    "experienceId": "your-experience-id",
    "userId": "customer@example.com",
    "sessionId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "metadata": {
      "source": "api-test",
      "timestamp": "2025-10-28T10:30:00Z"
    }
  }'

Authenticated Query

curl -X POST "https://nexus-api.uat.knowbl.com/api/v2/query" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -d '{
    "text": "What are your business hours?",
    "experienceId": "YOUR_EXPERIENCE_ID"
  }'

Error Handling

The query API returns standard HTTP status codes with detailed error messages:

400 Bad Request

Request validation failed. Common causes:

  • Invalid JSON: Malformed request body
  • Missing required fields: text or experienceId not provided
  • Invalid field values:
    • experienceId is not a valid UUID
    • sessionId is not a valid UUID
    • userId contains whitespace or control characters
    • userId exceeds 255 characters
  • Inactive workspace: The experience’s workspace is not active

Example:

{
  "statusCode": 400,
  "error": "Bad Request",
  "message": "Validation failed",
  "timestamp": "2025-10-28T10:30:00.000Z",
  "path": "/v2/query"
}

401 Unauthorized

Authentication required but not provided, or the provided access token is invalid.

Common causes:

  • Experience requires authentication but no token was provided
  • Access token is expired or invalid
  • Access token is missing the query:execute scope

Example:

{
  "statusCode": 401,
  "error": "Unauthorized",
  "message": "Access token required for this experience",
  "timestamp": "2025-10-28T10:30:00.000Z",
  "path": "/v2/query"
}

Solution:

  • Obtain a valid access token via POST /v2/auth/access-tokens
  • Ensure the token has the query:execute scope
  • Include the token in the Authorization: Bearer <token> header

403 Forbidden

Request forbidden for one of the following reasons:

1. Origin not allowed: The request origin doesn’t match any patterns in the experience’s allowedOrigins configuration.

Example:

{
  "statusCode": 403,
  "error": "Forbidden",
  "message": "Origin https://unauthorized-site.com is not allowed for this experience",
  "timestamp": "2025-10-28T10:30:00.000Z",
  "path": "/v2/query"
}

Solution: Contact the workspace administrator to add your domain to the allowed origins list in the experience configuration.

2. Insufficient scopes: Access token provided but missing required query:execute scope.

Example:

{
  "statusCode": 403,
  "error": "Forbidden",
  "message": "Insufficient scopes",
  "required": [
    "query:execute"
  ],
  "missing": [
    "query:execute"
  ],
  "timestamp": "2025-10-28T10:30:00.000Z",
  "path": "/v2/query"
}

Solution: Request a new access token with the query:execute scope included.

404 Not Found

Experience not found. The provided experienceId doesn’t exist or has been deleted.

Example:

{
  "statusCode": 404,
  "error": "Not Found",
  "message": "Experience not found",
  "timestamp": "2025-10-28T10:30:00.000Z",
  "path": "/v2/query"
}

500 Internal Server Error

Unexpected server error occurred while processing the query. These errors are logged for investigation.

Example:

{
  "statusCode": 500,
  "error": "Internal Server Error",
  "message": "An unexpected error occurred",
  "timestamp": "2025-10-28T10:30:00.000Z",
  "path": "/v2/query"
}

Note: If you receive this error, the queryId in your last successful response can help with debugging.

Best Practices

Session Management

Store the session ID immediately:

const response = await fetch("https://nexus-api.uat.knowbl.com/api/v2/query", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({
    text: "Hello",
    experienceId: "your-experience-id",
  }),
});

const data = await response.json();

// Store for subsequent queries
if (data.sessionId) {
  localStorage.setItem("nexus-session-id", data.sessionId);
}

Use the same session ID for the entire conversation:

const sessionId = localStorage.getItem("nexus-session-id");

const response = await fetch("https://nexus-api.uat.knowbl.com/api/v2/query", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({
    text: "What about refund policies?",
    experienceId: "your-experience-id",
    sessionId: sessionId, // Maintains context
  }),
});

Origin Allowlist Configuration

If you control the experience configuration, set up origin validation to prevent unauthorized access:

  1. Navigate to experience settings in the admin UI
  2. Add allowed origin patterns under Safety Settings
  3. Patterns support wildcards: https://*.example.com

Without origin validation: Anyone can query your experience from any website.

With origin validation: Only requests from allowed domains will succeed.

Metadata Usage Patterns

Track user journey:

metadata: {
  source: "checkout-page",
  cartValue: 129.99,
  itemCount: 3,
  step: "payment"
}

Monitor application context:

metadata: {
  appVersion: "2.1.0",
  platform: "iOS",
  networkType: "wifi",
  screenSize: "375x812"
}

Campaign attribution:

metadata: {
  campaign: "summer-sale-2025",
  utmSource: "email",
  utmMedium: "newsletter",
  utmContent: "cta-button"
}

Widget Integration

The Nexus Chat Widget automatically handles query submission, session management, error handling, and authentication. See the Chat Widget documentation for integration details.

Key widget benefits:

  • Automatic session ID persistence
  • Built-in error handling and retries
  • Origin management
  • Optional access token support
  • Message rendering with markdown support
  • Action button handling

Widget authentication:

The widget supports optional authentication via the accessToken configuration:

// Initialize with access token
window.NexusChatWidget.init({
experienceId: 'YOUR_EXPERIENCE_ID',
apiUrl: 'https://api.your-domain.com',
accessToken: 'YOUR_ACCESS_TOKEN'
});

// Update token dynamically
widget.updateAccessToken('NEW_ACCESS_TOKEN');

Error Recovery

Handle network errors gracefully:

try {
  const response = await fetch("https://nexus-api.uat.knowbl.com/api/v2/query", {
    /* ... */
  });

  if (!response.ok) {
    const error = await response.json();
    console.error("Query failed:", error.message);
    // Show user-friendly error message
    return;
  }

  const data = await response.json();
  // Process successful response
} catch (error) {
  console.error("Network error:", error);
  // Show offline message
}

Implement retry logic for transient failures:

async function queryWithRetry(body, maxRetries = 3) {
  for (let i = 0; i < maxRetries; i++) {
    try {
      const response = await fetch("https://nexus-api.uat.knowbl.com/api/v2/query", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify(body),
      });

      if (response.ok) {
        return await response.json();
      }

      // Don't retry client errors (4xx)
      if (response.status >= 400 && response.status < 500) {
        throw new Error(await response.text());
      }

      // Retry server errors (5xx)
      if (i < maxRetries - 1) {
        await new Promise((resolve) => setTimeout(resolve, 1000 * (i + 1)));
        continue;
      }
    } catch (error) {
      if (i === maxRetries - 1) {
        throw error;
      }
      await new Promise((resolve) => setTimeout(resolve, 1000 * (i + 1)));
    }
  }
}

Integration with Sessions

The query API works seamlessly with the session management API for multi-turn conversations and advanced tracking.

Automatic vs. Explicit Session Creation

Automatic (recommended for simple use cases):

  • Don’t provide a sessionId in your first query
  • The API automatically creates a session and returns the sessionId
  • Store and reuse this sessionId in subsequent queries

Explicit (for advanced control):

  • Pre-create sessions using the Sessions API before the first query
  • Useful when you need to set initial metadata or track session creation separately
  • Requires bearer token authentication (see Authentication Guide)
// 1. Create a session
const sessionResponse = await fetch("https://nexus-api.uat.knowbl.com/api/v2/sessions", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    Authorization: `Bearer ${apiKey}`,
  },
  body: JSON.stringify({
    experienceId: "your-experience-id",
    userId: "customer@example.com",
    metadata: {
      source: "support-chat",
      agent: "none",
    },
  }),
});

const session = await sessionResponse.json();

// 2. Use session ID in queries
const queryResponse = await fetch("https://nexus-api.uat.knowbl.com/api/v2/query", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({
    text: "How do I reset my password?",
    experienceId: "your-experience-id",
    sessionId: session.id,
  }),
});

Advanced Session Management

For production applications, you may want to:

  • Update session metadata as conversations progress to track state changes
  • Mark sessions as complete when conversations end to improve analytics
  • List and retrieve sessions for reporting and debugging

For detailed information on session lifecycle, metadata management, and session state transitions, see the Sessions documentation.

Next Steps