Skip to main content

Session Termination

Sessions end when their expiration time is reached, when the user manually ends the session, or when an error occurs. Your application must handle termination gracefully by cleaning up state, saving user work, and optionally redirecting back to the marketplace.

Termination Flow

sequenceDiagram
participant Timer as Session Timer
participant SDK as Provider SDK
participant App as Your Application
participant User
participant Marketplace as GW Marketplace

alt Session Expired
Timer->>SDK: Expiration reached
else User Ends Manually
User->>App: Clicks "End Session"
App->>SDK: sdk.endSession('manual')
else Error Occurs
SDK->>SDK: Unrecoverable error detected
end

SDK->>App: onSessionEnd(context) fires
App->>App: Save user work
App->>App: Clear authentication state
App->>App: Clear cached data
App->>App: Log analytics
App-->>User: Show farewell message
SDK->>Marketplace: Redirect (if configured)

Termination Reasons

The onSessionEnd hook receives a reason field indicating why the session ended:

ReasonDescriptionTypical Handling
expiredSession time elapsed naturallyShow "session expired" message, redirect
manualUser or app ended the session explicitlyShow "goodbye" message, redirect
errorAn unrecoverable error occurredShow error message, offer retry

Handling Termination with the SDK

The onSessionEnd Hook

This is the primary hook for session cleanup. It fires regardless of how the session ended:

const sdk = new MarketplaceSDK({
applicationId: 'your-app-id',
apiKey: 'gwsk_...',
environment: 'production',
hooks: {
onSessionEnd: async (context) => {
console.log('[Session] Ending. Reason:', context.reason);
console.log('[Session] Duration:', context.actualDurationMinutes, 'min');

// 1. Save any unsaved user work
await autoSaveUserWork();

// 2. Log session analytics
await logSessionEnd({
userId: context.userId,
sessionId: context.sessionId,
duration: context.actualDurationMinutes,
reason: context.reason,
timestamp: Date.now(),
});

// 3. Clear authentication state
localStorage.removeItem('auth_token');
localStorage.removeItem('refresh_token');
localStorage.removeItem('user_data');

// 4. Clear cached application data
await clearUserCache();

// 5. Reset application UI state
appState.reset();

console.log('[Session] Cleanup complete');
},
},
});

Context Object

interface SessionEndContext {
sessionId: string;
userId: string;
reason: 'expired' | 'manual' | 'error';
actualDurationMinutes: number;
}

Manually Ending a Session

Your application can programmatically end a session at any time:

// End with a reason
await sdk.endSession('manual');

// Common scenarios for manual termination:
// - User clicks a "Leave Session" button
// - User completes their workflow early
// - Application detects inactivity

Adding an "End Session" Button

function EndSessionButton({ sdk }) {
const handleEnd = async () => {
const confirmed = window.confirm(
'Are you sure you want to end your session?'
);
if (confirmed) {
await sdk.endSession('manual');
}
};

return <button onClick={handleEnd}>End Session</button>;
}

Redirect Behavior

By default, the SDK redirects users to the marketplace after session termination.

Configuring the Redirect URL

const sdk = new MarketplaceSDK({
applicationId: 'your-app-id',
apiKey: 'gwsk_...',
marketplaceUrl: 'https://platform.generalwisdom.com',
// Users are redirected here after session ends
});

Disabling Automatic Redirect

If you want to handle navigation yourself:

const sdk = new MarketplaceSDK({
applicationId: 'your-app-id',
apiKey: 'gwsk_...',
marketplaceUrl: undefined, // Disable automatic redirect
hooks: {
onSessionEnd: async (context) => {
await cleanup();

// Custom redirect logic
if (context.reason === 'expired') {
window.location.href =
'https://platform.generalwisdom.com?session=expired';
} else {
window.location.href = 'https://platform.generalwisdom.com';
}
},
},
});

Adding a Delay Before Redirect

Show a farewell message before redirecting:

onSessionEnd: async (context) => {
await cleanup();

// Show farewell UI
showFarewellScreen(context.reason);

// Redirect after delay
setTimeout(() => {
window.location.href = 'https://platform.generalwisdom.com';
}, 3000); // 3-second delay
},

Cleanup Checklist

When a session ends, ensure your application:

TaskWhyPriority
Save unsaved user workPrevent data lossCritical
Clear auth tokensPrevent unauthorized accessCritical
Log session analyticsTrack usage and behaviorHigh
Clear user cacheFree memory, ensure privacyHigh
Close open connectionsPrevent resource leaksMedium
Reset UI to unauthenticated statePrepare for next sessionMedium
Cancel pending network requestsAvoid errors after teardownLow

Error Handling During Cleanup

Cleanup should be resilient. Never let a single failure prevent the rest of the cleanup:

onSessionEnd: async (context) => {
const errors: Error[] = [];

// Each step is independent - continue even if one fails
try {
await autoSaveUserWork();
} catch (err) {
errors.push(err as Error);
console.error('[Cleanup] Failed to save work:', err);
}

try {
await logSessionAnalytics(context);
} catch (err) {
errors.push(err as Error);
console.error('[Cleanup] Failed to log analytics:', err);
}

// These operations are synchronous and should not fail
localStorage.removeItem('auth_token');
localStorage.removeItem('refresh_token');
sessionStorage.clear();

try {
await clearUserCache();
} catch (err) {
errors.push(err as Error);
console.error('[Cleanup] Failed to clear cache:', err);
}

if (errors.length > 0) {
console.warn(`[Cleanup] Completed with ${errors.length} errors`);
}
},

Handling Termination Without the SDK

If you are managing the session manually (without the Provider SDK), set up a timer that monitors the JWT expiration:

function startSessionMonitor(expiresAt: number) {
const checkInterval = setInterval(() => {
const now = Math.floor(Date.now() / 1000);
const remaining = expiresAt - now;

if (remaining <= 0) {
clearInterval(checkInterval);
handleSessionExpired();
} else if (remaining <= 300) {
// 5 minutes warning
showExpirationWarning(remaining);
}
}, 1000);

return () => clearInterval(checkInterval);
}

async function handleSessionExpired() {
// Perform cleanup
await autoSaveUserWork();
localStorage.removeItem('auth_token');
sessionStorage.clear();

// Show message and redirect
showNotification('Your session has expired.');
setTimeout(() => {
window.location.href = 'https://platform.generalwisdom.com';
}, 2000);
}

Session End UX Patterns

Expired Session

function showExpiredScreen() {
renderOverlay({
title: 'Session Expired',
message: 'Your session time has ended. Visit the marketplace to start a new session.',
actions: [
{ label: 'Return to Marketplace', href: 'https://platform.generalwisdom.com' },
{ label: 'Purchase New Session', href: 'https://platform.generalwisdom.com/apps/your-app' },
],
});
}

Manual End

function showGoodbyeScreen() {
renderOverlay({
title: 'Session Ended',
message: 'Thank you for using our application. Your work has been saved.',
actions: [
{ label: 'Return to Marketplace', href: 'https://platform.generalwisdom.com' },
],
});
}

Next Steps