Instances
Integrate Instances
This guide walks you through integrating CashIn's Instance endpoints into your application. Instance endpoints manage user sessions, track campaign participation, and handle state transitions from anonymous browsing to authenticated experiences.
Instance Endpoints
|
Endpoint |
Method |
Purpose |
Key Parameters |
Response Data |
|---|---|---|---|---|
|
|
POST |
Create temporary instance for anonymous users |
|
|
|
|
POST |
Check if instance is valid and active |
|
|
|
|
POST |
Extend expiration time of active instance |
|
|
|
|
POST |
Immediately invalidate an instance |
|
|
Authentication Setup
All Instance endpoints require Bearer token authentication using your partner secret key.
const headers = {
'Authorization': 'Bearer sk_live_abc123def456...',
'Content-Type': 'application/json',
'X-Request-ID': generateUniqueId() // Optional but recommended for debugging
};
Integration Patterns
Pattern 1: Anonymous User Tracking
Start tracking users immediately when they land on your site, before any login or signup.
// When user first visits your site
async function initializeAnonymousTracking(campaignId = null, insiderId = null) {
const fingerprintId = await generateFingerprint(); // Your fingerprinting logic
try {
const response = await fetch('/partner/v1/instance/anonymous', {
method: 'POST',
headers,
body: JSON.stringify({
fingerprint_id: fingerprintId,
campaign_id: campaignId, // Optional: if user came from a specific campaign
insider_id: insiderId // Optional: if referred by another user
})
});
const result = await response.json();
if (result.success) {
// Store instance ID in localStorage or session
localStorage.setItem('cashin_instance_id', result.data.instance_id);
localStorage.setItem('cashin_expires_at', result.data.expires_at);
return result.data.instance_id;
}
} catch (error) {
console.error('Failed to create anonymous instance:', error);
}
}
Pattern 2: Instance Validation
Always validate instances before making critical operations or applying rewards.
async function validateCurrentInstance() {
const instanceId = localStorage.getItem('cashin_instance_id');
if (!instanceId) {
return { valid: false, reason: 'No instance found' };
}
try {
const response = await fetch('/partner/v1/instance/validate', {
method: 'POST',
headers,
body: JSON.stringify({
instance_id: instanceId
})
});
const result = await response.json();
if (result.success && result.data.valid) {
return {
valid: true,
instance: result.data.instance,
state: result.data.instance.instance_state
};
} else {
// Instance expired or invalid - clean up local storage
localStorage.removeItem('cashin_instance_id');
localStorage.removeItem('cashin_expires_at');
return { valid: false, reason: result.data.reason };
}
} catch (error) {
console.error('Instance validation failed:', error);
return { valid: false, reason: 'Validation error' };
}
}
Pattern 3: Session Extension
Extend instances when users show continued engagement.
async function extendInstanceSession() {
const instanceId = localStorage.getItem('cashin_instance_id');
if (!instanceId) return false;
try {
const response = await fetch('/partner/v1/instance/extend', {
method: 'POST',
headers,
body: JSON.stringify({
instance_id: instanceId
})
});
const result = await response.json();
if (result.success) {
// Update stored expiration time
localStorage.setItem('cashin_expires_at', result.data.new_expires_at);
console.log(`Instance extended by ${result.data.extended_by_hours} hours`);
return true;
}
} catch (error) {
console.error('Failed to extend instance:', error);
}
return false;
}
// Trigger extension on significant user actions
document.addEventListener('click', debounce(extendInstanceSession, 300000)); // Every 5 minutes max
Pattern 4: Clean Instance Termination
Properly revoke instances when users log out or leave.
async function terminateInstance() {
const instanceId = localStorage.getItem('cashin_instance_id');
if (!instanceId) return;
try {
const response = await fetch('/partner/v1/instance/revoke', {
method: 'POST',
headers,
body: JSON.stringify({
instance_id: instanceId
})
});
const result = await response.json();
if (result.success) {
console.log('Instance revoked successfully');
}
} catch (error) {
console.error('Failed to revoke instance:', error);
} finally {
// Always clean up local storage
localStorage.removeItem('cashin_instance_id');
localStorage.removeItem('cashin_expires_at');
}
}
// Call on logout or page unload
window.addEventListener('beforeunload', terminateInstance);
Complete Integration Workflow
Step 1: Initialize on Page Load
async function initializeCashInTracking() {
// Check for existing valid instance
const validation = await validateCurrentInstance();
if (validation.valid) {
console.log('Existing instance found:', validation.instance.id);
return validation.instance.id;
}
// Extract campaign info from URL if present
const urlParams = new URLSearchParams(window.location.search);
const campaignId = urlParams.get('campaign_id');
const insiderId = urlParams.get('insider_id');
// Create new anonymous instance
return await initializeAnonymousTracking(campaignId, insiderId);
}
// Run on every page load
document.addEventListener('DOMContentLoaded', initializeCashInTracking);
Step 2: Handle User State Transitions
// When user signs up or logs in
async function handleUserAuthentication(userId) {
const instanceId = localStorage.getItem('cashin_instance_id');
if (instanceId) {
// Validate current instance before proceeding
const validation = await validateCurrentInstance();
if (validation.valid) {
// Instance will automatically upgrade to authenticated state
// when CashIn detects the user login on your backend
console.log('Instance will upgrade to authenticated state');
// Optionally extend the session for authenticated users
await extendInstanceSession();
} else {
// Create new instance for authenticated user
await initializeAnonymousTracking();
}
}
}
Step 3: Implement Error Handling
class CashInInstanceManager {
constructor(secretKey) {
this.secretKey = secretKey;
this.headers = {
'Authorization': `Bearer ${secretKey}`,
'Content-Type': 'application/json'
};
}
async makeRequest(endpoint, data) {
const requestId = this.generateRequestId();
try {
const response = await fetch(endpoint, {
method: 'POST',
headers: {
...this.headers,
'X-Request-ID': requestId
},
body: JSON.stringify(data)
});
const result = await response.json();
// Handle common errors
if (!response.ok) {
switch (response.status) {
case 401:
throw new Error('Invalid API key - check your authentication');
case 429:
throw new Error('Rate limit exceeded - please retry after delay');
case 400:
throw new Error(`Bad request: ${result.message}`);
default:
throw new Error(`API error: ${result.message}`);
}
}
return result;
} catch (error) {
console.error(`Request ${requestId} failed:`, error);
throw error;
}
}
generateRequestId() {
return `req_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
}
}
Best Practices
Rate Limiting Compliance
// Implement exponential backoff for rate limits
async function withRateLimit(apiCall, maxRetries = 3) {
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
return await apiCall();
} catch (error) {
if (error.message.includes('Rate limit') && attempt < maxRetries) {
const delay = Math.pow(2, attempt) * 1000; // Exponential backoff
await new Promise(resolve => setTimeout(resolve, delay));
continue;
}
throw error;
}
}
}
Instance Lifecycle Management
class InstanceLifecycleManager {
constructor(apiManager) {
this.api = apiManager;
this.instanceId = null;
this.expiresAt = null;
this.checkInterval = null;
}
async initialize() {
// Load from storage
this.instanceId = localStorage.getItem('cashin_instance_id');
this.expiresAt = localStorage.getItem('cashin_expires_at');
// Validate existing instance
if (this.instanceId && this.expiresAt) {
const isValid = await this.validateInstance();
if (!isValid) {
await this.createNewInstance();
}
} else {
await this.createNewInstance();
}
// Set up automatic validation checks
this.startPeriodicValidation();
}
startPeriodicValidation() {
// Check instance validity every 10 minutes
this.checkInterval = setInterval(async () => {
if (this.instanceId) {
const isValid = await this.validateInstance();
if (!isValid) {
clearInterval(this.checkInterval);
await this.createNewInstance();
this.startPeriodicValidation();
}
}
}, 600000); // 10 minutes
}
async validateInstance() {
try {
const result = await this.api.makeRequest('/partner/v1/instance/validate', {
instance_id: this.instanceId
});
return result.success && result.data.valid;
} catch (error) {
return false;
}
}
async createNewInstance() {
try {
const result = await this.api.makeRequest('/partner/v1/instance/anonymous', {
fingerprint_id: await this.generateFingerprint(),
campaign_id: this.extractCampaignId(),
insider_id: this.extractInsiderId()
});
if (result.success) {
this.instanceId = result.data.instance_id;
this.expiresAt = result.data.expires_at;
localStorage.setItem('cashin_instance_id', this.instanceId);
localStorage.setItem('cashin_expires_at', this.expiresAt);
}
} catch (error) {
console.error('Failed to create new instance:', error);
}
}
cleanup() {
if (this.checkInterval) {
clearInterval(this.checkInterval);
}
if (this.instanceId) {
// Attempt to revoke instance
this.api.makeRequest('/partner/v1/instance/revoke', {
instance_id: this.instanceId
}).catch(error => {
console.warn('Failed to revoke instance on cleanup:', error);
});
}
localStorage.removeItem('cashin_instance_id');
localStorage.removeItem('cashin_expires_at');
}
}
Testing Your Integration
Unit Test Example
// Test instance creation and validation flow
async function testInstanceFlow() {
const manager = new CashInInstanceManager('sk_test_...');
console.log('Testing instance creation...');
const createResult = await manager.makeRequest('/partner/v1/anonymous', {
fingerprint_id: 'test-fingerprint-123',
campaign_id: null,
insider_id: null
});
console.log('Instance created:', createResult.data.instance_id);
console.log('Testing instance validation...');
const validateResult = await manager.makeRequest('/partner/v1/instance/validate', {
instance_id: createResult.data.instance_id
});
console.log('Validation result:', validateResult.data.valid);
console.log('Testing instance extension...');
const extendResult = await manager.makeRequest('/partner/v1/instance/extend', {
instance_id: createResult.data.instance_id
});
console.log('Extension result:', extendResult.data.new_expires_at);
console.log('Testing instance revocation...');
const revokeResult = await manager.makeRequest('/partner/v1/instance/revoke', {
instance_id: createResult.data.instance_id
});
console.log('Revocation result:', revokeResult.success);
}
Debugging Tips
-
Always include X-Request-ID headers for easier debugging with CashIn support
-
Log instance state transitions to understand user journey flow
-
Monitor instance expiration times to optimize extension timing
-
Track validation failures to identify integration issues
-
Use test API keys during development to avoid affecting production data
Common Integration Issues
Issue: Instance expires too quickly Solution: Implement proactive extension based on user activity patterns
Issue: Multiple instances created for same user Solution: Always validate existing instances before creating new ones
Issue: Campaign attribution not working Solution: Ensure campaign_id is properly extracted from URLs and passed during instance creation
Issue: Instance state not upgrading after login Solution: Verify your backend is properly communicating user authentication to CashIn
This integration guide provides the foundation for implementing Instance endpoints in your application. The instance system will automatically handle state transitions and campaign attribution once properly integrated.
Instance Endpoint Summary
Create Anonymous Instance
-
Use Case: Start tracking users before login/signup
-
Returns: New instance ID with 24-hour default expiration
-
Instance State:
anonymous
Validate Instance
-
Use Case: Check session validity before operations
-
Returns: Instance details including state, user info, campaign data
-
States:
anonymous,signup,authenticated
Extend Instance
-
Use Case: Keep active users' sessions alive
-
Returns: New expiration time (typically +24 hours)
-
Limitation: Cannot extend expired instances
Revoke Instance
-
Use Case: Clean logout, security revocation
-
Returns: Revocation timestamp
-
Effect: Instance becomes immediately invalid