Skip to Content
SessionsSession Lifecycle

Session Lifecycle

Understanding how sessions progress through their lifecycle is essential for effective session management.

Lifecycle States

Sessions can be in one of three states:

1. Active

The default state when a session is created. An active session:

  • Can receive new queries
  • Accumulates conversation turns
  • Can have metadata updated

Characteristics:

  • status: "active"
  • completedAt: null

2. Completed

A terminal state indicating the session was explicitly finished by the client:

Characteristics:

  • status: "completed"
  • completedAt is set to completion timestamp
  • Cannot be reopened or receive new queries
  • Metadata cannot be updated
  • Indicates successful conversation conclusion

Use Cases:

  • User explicitly ends chat session
  • Conversation reaches natural conclusion
  • User navigates away from page and signals end
  • User objective is met or conversation workflow completes

3. Expired

A terminal state indicating the session was marked as expired by the client application:

Characteristics:

  • status: "expired"
  • completedAt is set to the timestamp when the client marked it expired
  • Cannot be reopened or receive new queries
  • Metadata cannot be updated
  • Indicates the client application determined the session should be expired based on business rules

Use Cases:

  • Client implements time-based expiration (e.g., 24 hours since creation)
  • Client implements inactivity-based expiration (e.g., 30 minutes idle)
  • User abandons conversation and client cleanup policy triggers
  • Client business logic determines the session should end

State Transition Diagram

┌─────────┐ │ Created │ └────┬────┘ v ┌─────────┐ │ active │ ───────────────────────────────┐ └────┬────┘ │ │ │ │ POST /complete │ POST /complete │ (status=completed) │ (status=expired) │ │ v v ┌───────────┐ ┌──────────┐ │ completed │ │ expired │ └───────────┘ └──────────┘ │ │ └────────────┬────────────────────────┘ (terminal)

Key Transition Rules:

  • active → completed: Client calls completion endpoint with status=completed
  • active → expired: Client calls completion endpoint with status=expired
  • Both terminal states are permanent (no transitions out)

Session Creation Patterns

There are two ways sessions can be created:

Pre-create sessions before the first query using the /v2/sessions endpoint:

// Create a new session
const response = await fetch("https://nexus-api.uat.knowbl.com/api/v2/sessions", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    Authorization: "Bearer YOUR_ACCESS_TOKEN",
  },
  body: JSON.stringify({
    experienceId: "your-experience-id",
    userId: "user@example.com",
    metadata: {
      source: "mobile-app",
      version: "2.0.1",
    },
  }),
});

const session = await response.json();
console.log("Session created:", session.id);

Benefits:

  • Full control over session timing
  • Can set metadata before first query
  • Enables session tracking for analytics
  • Supports pre-chat form data collection

Use Cases:

  • Chat widget initialization
  • Multi-step workflows
  • Authenticated user sessions
  • Session pre-warming for analytics

2. Implicit Creation

Sessions are automatically created during query processing if no sessionId is provided:

// Query without sessionId - creates implicit session
// Note: /query endpoint is public and does not require authentication
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",
  }),
});

const result = await response.json();
// result.sessionId contains the auto-created session ID

Characteristics:

  • Session created automatically by query API
  • Uses request metadata for initial context
  • sessionId returned in query response
  • Subsequent queries can reference this sessionId

Use Cases:

  • Simple one-off queries
  • Anonymous interactions
  • Embedded forms or quick questions
  • When session tracking is not required

Conversation Turns

Sessions store conversation history as a sequence of turns:

Turn Structure

Each turn represents one query-response pair:

{
  "turnNumber": 1,
  "query": {
    "text": "How do I reset my password?",
    "timestamp": "2025-10-28T12:00:00.000Z"
  },
  "response": {
    "answer": "To reset your password, click the 'Forgot Password' link...",
    "timestamp": "2025-10-28T12:00:01.500Z"
  }
}

Turn Sequencing

  • Turns are numbered sequentially starting at 1
  • Each turn is immutable once created
  • Turn order reflects conversation chronology
  • All turns within a session are included in conversation context while the session is active

Session Completion

When to Complete Sessions

Mark sessions as completed when:

  • User explicitly ends the conversation
  • Conversation workflow reaches natural conclusion
  • User session ends (logout, navigation)
  • Business logic determines the conversation is complete

How to Complete Sessions

Use the complete endpoint:

// Mark session as completed
const sessionId = "session-uuid";
const params = new URLSearchParams({
  experienceId: "your-experience-id",
});

const response = await fetch(
  `https://nexus-api.uat.knowbl.com/api/v2/sessions/${sessionId}/complete?${params}`,
  {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      Authorization: "Bearer YOUR_ACCESS_TOKEN",
    },
    body: JSON.stringify({
      status: "completed",
    }),
  },
);

const session = await response.json();
console.log("Session completed at:", session.completedAt);

Client-Controlled Session Expiration

The Nexus platform does not automatically expire sessions. Sessions remain in the active state indefinitely until the client application explicitly marks them as completed or expired. This design gives client applications full control over session lifecycle management based on their specific business requirements.

Expiration API

To mark a session as expired, use the session completion endpoint:

Endpoint: POST /v2/sessions/:sessionId/complete

Request:

POST /v2/sessions/{sessionId}/complete?experienceId={experienceId}
Authorization: Bearer {api-key}
Content-Type: application/json

{
  "status": "expired"
}

Response:

{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "experienceId": "660e8400-e29b-41d4-a716-446655440000",
  "status": "expired",
  "completedAt": "2025-10-29T15:30:45.123Z"
}

Authentication: Requires bearer token with sessions:complete scope

Widget Integration

After marking a session as expired via the API, clear the chat widget UI:

// Mark session as expired via API
const response = await fetch(
  `https://nexus-api.uat.knowbl.com/api/v2/sessions/${sessionId}/complete?experienceId=${experienceId}`,
  {
    method: "POST",
    headers: {
      Authorization: `Bearer ${apiKey}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ status: "expired" }),
  },
);

// Clear the widget UI
widget.clearChat();

The widget emits events that you can use for tracking:

widget.on("sessionClear", (data) => {
  console.log("Session cleared:", data.sessionId);
  console.log("Messages removed:", data.messageCount);
});

widget.on("sessionEnd", (data) => {
  console.log("Session ended after", data.duration, "ms");
});

Implementation Patterns

Client applications can implement various expiration strategies based on their requirements:

Pattern 1: Time-Based Expiration

Expire sessions after a fixed duration since creation:

// Configuration
const SESSION_TIMEOUT_MS = 24 * 60 * 60 * 1000; // 24 hours

// Track session creation time
const sessionCreatedAt = new Date(session.createdAt);

// Check if session should expire
async function checkSessionExpiration(sessionId, createdAt) {
  const now = Date.now();
  const age = now - new Date(createdAt).getTime();

  if (age > SESSION_TIMEOUT_MS) {
    // Expire the session
    await fetch(
      `https://nexus-api.uat.knowbl.com/api/v2/sessions/${sessionId}/complete?experienceId=${experienceId}`,
      {
        method: "POST",
        headers: {
          Authorization: `Bearer ${apiKey}`,
          "Content-Type": "application/json",
        },
        body: JSON.stringify({ status: "expired" }),
      },
    );
    // Clear the widget
    widget.clearChat();
  }
}

Pattern 2: Inactivity-Based Expiration

Expire sessions after a period of user inactivity:

// Configuration
const INACTIVITY_TIMEOUT_MS = 30 * 60 * 1000; // 30 minutes

// Track last activity time
let lastActivityTime = Date.now();

// Update activity time on user interactions
widget.on("messageComplete", () => {
  lastActivityTime = Date.now();
});

widget.on("querySent", () => {
  lastActivityTime = Date.now();
});

// Check for inactivity periodically
setInterval(async () => {
  const now = Date.now();
  const inactiveTime = now - lastActivityTime;
  const currentSessionId = widget.store.currentSessionId.value;

  if (inactiveTime > INACTIVITY_TIMEOUT_MS && currentSessionId) {
    // Expire the session
    await fetch(
      `https://nexus-api.uat.knowbl.com/api/v2/sessions/${currentSessionId}/complete?experienceId=${experienceId}`,
      {
        method: "POST",
        headers: {
          Authorization: `Bearer ${apiKey}`,
          "Content-Type": "application/json",
        },
        body: JSON.stringify({ status: "expired" }),
      },
    );

    // Clear the widget
    widget.clearChat();
  }
}, 60000); // Check every minute

Pattern 3: Event-Driven Expiration

Expire sessions based on application events:

// Example 1: Expire on user logout
async function onUserLogout() {
  const sessionId = widget.store.currentSessionId.value;

  if (sessionId) {
    await fetch(
      `https://nexus-api.uat.knowbl.com/api/v2/sessions/${sessionId}/complete?experienceId=${experienceId}`,
      {
        method: "POST",
        headers: {
          Authorization: `Bearer ${apiKey}`,
          "Content-Type": "application/json",
        },
        body: JSON.stringify({ status: "expired" }),
      },
    );

    widget.clearChat();
  }
}

// Example 2: Expire on page unload (if not persisting across page loads)
window.addEventListener("beforeunload", () => {
  const sessionId = widget.store.currentSessionId.value;

  if (sessionId && !shouldPersistSession) {
    // Use fetch with keepalive for reliable delivery with auth headers during page unload
    fetch(
      `https://nexus-api.uat.knowbl.com/api/v2/sessions/${sessionId}/complete?experienceId=${experienceId}`,
      {
        method: "POST",
        headers: {
          Authorization: `Bearer ${apiKey}`,
          "Content-Type": "application/json",
        },
        body: JSON.stringify({ status: "expired" }),
        keepalive: true, // Ensures request completes during page unload
      },
    ).catch(() => {
      // Silently fail - page is unloading anyway
    });
  }
});

Session Hijack Protection

The platform prevents session hijacking through userId validation:

How It Works

  1. Session Creation: userId is stored with session
  2. Session Reuse: Subsequent queries must include same userId
  3. Validation: System compares provided userId with session’s userId
  4. Enforcement: Mismatched userId results in error

userId Normalization

  • Email addresses: Case-insensitive (normalized to lowercase)
  • UUID format: Case-insensitive
  • Custom strings: Case-insensitive

Examples:

// These are considered the same userId:
"user@example.com";
"USER@EXAMPLE.COM";
"User@Example.Com";

Error Handling

Attempting to use a session with wrong userId:

{
  "statusCode": 403,
  "message": "Session hijack detected: userId mismatch"
}

Best Practices

Session Creation

  • Pre-create sessions for widget interactions (enables tracking and metadata)
  • Use meaningful userId values such as email addresses or customer IDs
  • Include initial metadata for tracking source, context, and analytics
  • Validate experienceId before session creation to catch configuration errors early

Session Management

  • Complete sessions explicitly when user closes widget or logs out
  • Use completed status for normal user-initiated termination
  • Use expired status for client-determined timeout scenarios or cleanup jobs
  • Don’t complete sessions that may resume later (e.g., page refresh, browser reopen)

Session Expiration

When implementing client-controlled expiration:

  • Choose the right pattern: Time-based for absolute limits, inactivity-based for engagement, event-driven for state changes
  • Coordinate API and UI: Always call the completion API before clearing the widget
  • Use appropriate timing: Check less frequently for time-based (every few minutes), more frequently for inactivity (every minute)
  • Handle edge cases: Account for page refreshes, browser tabs, and network failures
  • Warn users proactively: Give advance notice before expiring sessions, especially for inactivity
  • Track for analytics: Use widget events to monitor session lifecycle for optimization

Security & Session Hijacking

  • Always pass the same userId when reusing sessions across requests
  • Store userId alongside sessionId in client state to ensure consistency
  • Use consistent userId format (e.g., always lowercase for email addresses)
  • Handle 403 hijack errors by creating a new session rather than retrying

Multi-turn Conversations

  • Always pass sessionId for follow-up queries to maintain conversation context
  • Monitor turn count for very long conversations (consider completion for performance)
  • Preserve important context in metadata rather than relying only on conversation history

Error Handling

  • Handle 404 errors by creating a new session (previous session may have expired)
  • Implement retry logic for transient failures with exponential backoff
  • Cache session data client-side to reduce API calls and improve resilience

Performance

  • Batch metadata updates when possible to reduce API calls
  • Use pagination and filtering efficiently when listing sessions (don’t fetch all sessions)
  • Monitor rate limits and implement backoff strategies to prevent throttling

Next Steps