Skip to main content

Overview & Concepts

What is Visitor Mode?

Visitor mode enables anonymous public access to your community, allowing users to browse and discover content without requiring authentication. This feature is ideal for growth funnels, SEO optimization, and public content discovery while maintaining platform security and stability.

Visitor Users

Purpose: Anonymous users who browse public content
Access: Read-only permissions enforced server-side
Tracking: Identified by device fingerprinting
Use Case: Public content discovery, growth funnel

Bot Users

Purpose: Search engine crawlers and automated indexers
Access: Read-only permissions for content indexing
Tracking: Identified by User-Agent analysis
Use Case: SEO optimization, content discoverability

How Visitor Mode Works

Visitor mode uses device fingerprinting to identify anonymous users while maintaining privacy:
1

Device Identification

Your app generates or retrieves a unique device fingerprint for the anonymous user
2

Visitor Login

The SDK logs in the user as a visitor using the device fingerprint, with optional secure mode via authSignature
3

Server-Side Role Assignment

social.plus server assigns the “Visitor” or “Bot” role based on the request (User-Agent for bots)
4

Read-Only Access

Server-side permissions enforce read-only access, allowing content discovery without modification capabilities
Why Device Fingerprinting? This approach allows tracking unique anonymous visitors for analytics while maintaining privacy. Visitors are restricted to read-only access, protecting community integrity.

User Type Comparison

Understanding the different user types helps you design the right access patterns:
  • Signed-In Users
  • Visitor Users
  • Bot Users
// Full authenticated user with read/write access
import { Client } from '@amityco/ts-sdk';

const client = Client.createClient('apiKey', 'apiRegion');

// ... your app setup code

await Client.login({
  userId: 'user-123',
  displayName: 'John Doe',
}, sessionHandler);
Capabilities:
  • ✅ Full read/write access to all features
  • ✅ Real-time event connections (MQTT)
  • ✅ Push notifications
  • ✅ Create posts, comments, reactions
  • ✅ Join communities and follow users

Quick Start (Visitor Mode)

Step 1: Initialize the SDK

Start by setting up the social.plus client with your API key:
let client = try! AmityClient(apiKey: "your-api-key", region: .SG)

Step 2: Get Visitor Device ID

Generate or retrieve a unique device identifier for the visitor:
let deviceId = client.getVisitorDeviceId()
print("Device ID: \(deviceId)")
The device ID is automatically generated and cached on first access. This unique identifier is used to track the visitor session.

Step 3: Login as Visitor

Authenticate as an anonymous visitor to access public content:
Task { @MainActor in
    do {
        // Simple visitor login (development)
        try await client.loginAsVisitor(
            authSignature: nil,
            authSignatureExpiresAt: nil,
            sessionHandler: visitorSessionHandler
        )
        print("Visitor login successful")
    } catch {
        print("Visitor login failed: \(error)")
    }
}

Step 4: Login as Bot (TypeScript Only)

For search engine crawlers and automated indexers:
TypeScript
try {
    await Client.loginAsBot({ sessionHandler });
    console.log('Bot login successful');
} catch (error) {
    console.error('Bot login failed:', error);
}
Bot login is automatically determined by User-Agent analysis on the server. Use this method when you need explicit bot role assignment.

Step 5: Check User Type

Verify the current user type to adapt your UI accordingly:
let userType = client.currentUserType
switch userType {
case .signedIn:
    print("User is authenticated")
case .visitor:
    print("User is a visitor")
case .bot:
    print("User is a bot")
}

Step 6: Logout

End the visitor session:
do {
    try await client.secureLogout()
} catch {
    /// Handle error from revoking accessToken here
}

Secure Visitor Mode (Production)

For production environments, implement secure visitor authentication with auth signatures:

Backend Auth Signature Generation

Your backend must generate auth signatures to verify visitor sessions:
// Example Node.js backend
const crypto = require('crypto');

app.post('/api/visitor/auth-signature', async (req, res) => {
  const { deviceId } = req.body;
  
  // Set expiration (e.g., 1 hour from now)
  const authSignatureExpiresAt = new Date(Date.now() + 3600000).toISOString();
  
  // Create signature using HMAC-SHA256
  const message = `deviceId=${deviceId}&authSignatureExpiresAt=${authSignatureExpiresAt}`;
  const authSignature = crypto
    .createHmac('sha256', process.env.SOCIAL_PLUS_APP_SECRET)
    .update(message)
    .digest('hex');
  
  res.json({
    authSignature,
    authSignatureExpiresAt
  });
});

Secure Visitor Login

Use auth signatures for production visitor sessions:
Task { @MainActor in
    do {
        // Get device ID
        let deviceId = client.getVisitorDeviceId()
        
        // Request auth signature from your backend
        let (signature, expiresAt) = try await fetchAuthSignature(deviceId: deviceId)
        
        // Login with secure mode
        try await client.loginAsVisitor(
            authSignature: signature,
            authSignatureExpiresAt: expiresAt,
            sessionHandler: visitorSessionHandler
        )
        print("Secure visitor login successful")
    } catch {
        print("Secure visitor login failed: \(error)")
    }
}

Session Handler for Token Renewal

Implement session handlers to automatically refresh auth signatures:
  • iOS
  • Android
  • TypeScript
class VisitorSessionHandler: AmitySessionHandler {
    func sessionWillRenewAccessToken(renewal: AccessTokenRenewal) {
        let deviceId = client.getVisitorDeviceId()
        
        // Fetch new auth signature from your backend
        AuthService.shared.fetchVisitorAuthSignature(deviceId: deviceId) { result in
            switch result {
            case .success(let authData):
                renewal.renewWithAuthSignature(
                    authSignature: authData.signature,
                    authSignatureExpiresAt: authData.expiresAt
                )
            case .failure(let error):
                print("Failed to refresh visitor token: \(error)")
                renewal.unableToRetrieveAuthSignature()
            }
        }
    }
}

// Use during visitor login
let sessionHandler = VisitorSessionHandler()
try await client.loginAsVisitor(
    authSignature: signature,
    authSignatureExpiresAt: expiresAt,
    sessionHandler: sessionHandler
)

Understanding Visitor Permissions

Visitor and bot users have server-side enforced read-only permissions to protect community integrity:

Allowed Actions ✅

  • View public posts and content
  • Browse public communities
  • View user profiles
  • View comments and replies
  • View post reactions
  • Access public media (images, videos)

Restricted Actions ❌

  • Create posts or stories
  • Comment or reply
  • React to posts/comments
  • Join communities
  • Follow/unfollow users
  • Report content or users
  • Send messages
  • Receive push notifications
  • Real-time event connections (MQTT)

Permission Enforcement

All visitor restrictions are enforced server-side - attempting restricted actions will result in permission errors:
// Error codes for visitor/bot permission denial
ServerError.VISITOR_PERMISSION_DENIED: 488999
ServerError.BOT_PERMISSION_DENIED: 488998

Resource Conservation

Visitors and bots are excluded from resource-intensive features:
  • Real-Time Events
  • Push Notifications
  • User Discovery
MQTT Connection: Disabled for visitors/bots
  • No real-time event subscriptions
  • No live updates or notifications
  • Reduces server load and connection costs
  • Does not count towards CCU (Concurrent Connection Users) limits
// SDK automatically skips MQTT connection for visitors
const userType = Client.getCurrentUserType();
if (userType === UserTypeEnum.VISITOR || userType === UserTypeEnum.BOT) {
    // mqtt.connect() is NOT called
}

UIKit Visitor Experience

UIKit automatically adapts to visitor mode with optimized navigation and content discovery:

Page Visibility

UIKit shows/hides pages based on user type:
  • Visitor Mode
  • Signed-In User
Visible Pages:
  • ✅ Explore (default homepage)
  • ✅ Clips Feed (configurable, hidden by default)
Hidden Pages:
  • ❌ Newsfeed
  • ❌ Notifications
  • ❌ My Communities
// config.json - Clips Feed visibility control
{
  "feature_flags": {
    "post": {
      "clip": {
        "can_create": "signed_in_user_only",  // or "all", "none"
        "can_view_tab": "signed_in_user_only" // or "all", "none"
      }
    }
  }
}

Adaptive Navigation

UIKit navigation automatically adjusts based on available pages:

Mobile Navigation

Single Tab Scenario: When only one page is visible (e.g., Explore only)
  • Top tab navigation bar is hidden
  • Cleaner, focused single-page experience
Multiple Tabs: When 2+ pages are visible
  • Top tab navigation bar remains visible
  • Users can switch between available pages

Desktop Navigation

Sidebar Always Visible: Regardless of page count
  • Main sidebar navigation always shown
  • Ensures persistent access to global elements
  • Includes search bar and other navigation tools
  • Maintains consistent desktop experience

Visitor Interaction Handling

UIKit provides default behaviors for visitor interaction attempts:
// AmityGlobalBehavior - Default visitor interaction handling

const { AmityGlobalBehavior } = usePageBehavior();

// Visitor user
AmityGlobalBehavior.handleGuestUserAction();
// Non-Member user in a community
AmityGlobalBehavior.handleNonMemberAction();
// Non-Follower user of a user
AmityGlobalBehavior.handleNonFollowerAction();

Action Button States

UIKit manages action button visibility based on user type and permissions:
ActionVisitorNon-MemberMember
React to PostButton visible, action blockedButton visible, action allowedButton visible, action allowed
Comment on PostButton visible, action blockedButton visible, action allowedButton visible, action allowed
Reply to CommentButton visible, action blockedButton visible, action allowedButton visible, action allowed
Report ContentButton visible, action blockedButton visible, action blockedButton visible, action allowed
Vote in PollButton visible, action blockedButton hidden, join requiredButton visible, action allowed
ActionVisitorSigned-In User
React to PostButton hiddenButton visible, action allowed
Comment on PostButton visible, action blockedButton visible, action allowed
Reply to CommentButton visible, action blockedButton visible, action allowed
Report PostButton visible, action blockedButton visible, action allowed
Follow UserButton visible, action blockedButton visible, action allowed
Block UserButton visible, action blockedButton visible, action allowed
ActionVisitorSigned-In User
React to StoryButton visible, action blockedButton visible, action allowed
React to ClipButton visible, action blockedButton visible, action allowed
Comment on ClipButton visible, action blockedButton visible, action allowed
ActionVisitorSigned-In User
Send MessageInput visible, action blockedInput visible, action allowed
React to MessageButton visible, action blockedButton visible, action allowed
Report MessageButton visible, action blockedButton visible, action allowed

Customizing Visitor Behavior

Override default behaviors to match your app’s UX:
// Example: Custom visitor action handling

 <AmityUIKitProvider
  apiKey="API_KEY"
  apiRegion="API_REGION"
  userId="userId"
  displayName="displayName"
  configs={config}
  pageBehavior={{
    AmityGlobalBehavior: {
        handleGuestUserAction: () => {
            // Overridable for custom behavior
        },
        handleNonMemberAction: () => {
            // Overridable for custom behavior
        },
        handleNonFollowerAction: () => {
            // Overridable for custom behavior
        },
    }
  }}
>
 <UikitComponent>
</AmityUIKitProvider>

Data Management & Lifecycle

Guest User Data Cleanup

To prevent accumulation of transient visitor data, social.plus automatically cleans up inactive guest users:
Schedule: Periodic cleanup (configurable, typically 30-60 days)Criteria: Guest users inactive for the defined periodProcess:
  • Scheduled job runs automatically
  • Identifies inactive guest user records
  • Permanently deletes inactive guest data
  • No manual intervention required
What’s Deleted:
  • Guest user profile records
  • Device fingerprint associations
  • Session history
  • Any cached visitor data
Active Visitors: Continuously using visitors retain their dataPrivacy Compliance: Automatic cleanup supports GDPR/privacy regulationsAnalytics Impact: Historical analytics remain unaffectedConversion Tracking: Converted visitors (who signed up) preserve their history
Event Availability: Guest user events are available through existing webhook/event observation mechanisms configured in your social.plus console.

Implementation Best Practices

Visitor Mode Strategy

Recommended Scenarios:
  1. Public Content Discovery
    • Community showcases and landing pages
    • SEO-optimized public content
    • Growth funnel entry points
    • Social media linked content
  2. Conversion Optimization
    • Allow browsing before signup
    • Demonstrate community value
    • Reduce friction in user journey
    • Track engagement before conversion
  3. SEO & Indexing
    • Enable search engine crawling
    • Improve content discoverability
    • Separate bot traffic from analytics
    • Optimize for organic search
Implementation Tips:
  • Set clear upgrade prompts for interactive features
  • Track visitor-to-member conversion rates
  • Monitor guest traffic patterns
  • Use analytics to optimize conversion flow
Require Sign-In For:
  1. Private/Sensitive Content
    • Member-only communities
    • Personal conversations
    • Restricted content
    • Premium features
  2. High-Value Interactions
    • Content creation
    • Community moderation
    • Direct messaging
    • Transaction-based features
  3. Compliance Requirements
    • Age-restricted content
    • Regulated industries
    • Terms of service acceptance
    • User accountability needs

Security Considerations

Always Use Secure Mode in Production:
// ✅ Production: Secure visitor mode with auth signatures
await client.loginAsVisitor(
  authSignature,        // Generated by your backend
  authSignatureExpiresAt, // With proper expiration
  sessionHandler        // With token renewal logic
);

// ❌ Development only: Simple visitor mode
await client.loginAsVisitor(); // No auth signature
Why Secure Mode?
  • Prevents unauthorized visitor creation
  • Enables server verification of device identity
  • Supports automatic token renewal
  • Maintains audit trail of visitor sessions
Privacy-First Approach:
  • Device IDs are anonymized
  • No personal information collected
  • Automatic cleanup of inactive visitors
  • Compliant with GDPR/CCPA requirements
Best Practices:
  • Disclose visitor tracking in privacy policy
  • Provide opt-out mechanisms where required
  • Use device IDs only for platform functionality
  • Don’t link device IDs to external identifiers

Performance Optimization

Visitor Mode Reduces Resource Usage:
  1. No Real-Time Connections
    • Visitors don’t connect to MQTT
    • Reduces concurrent connection load
    • Lower bandwidth consumption
    • Better scalability for public access
  2. No Push Notifications
    • Prevents notification system overhead
    • Reduces APNs/FCM API calls
    • Avoids costs from anonymous audience
    • Focuses notifications on engaged users
  3. Optimized Query Performance
    • Visitors excluded from user searches
    • Cleaner user discovery results
    • Faster query execution
    • Better database performance
Encourage Visitor-to-Member Conversion:
// Example: Strategic upgrade prompts
const handleVisitorAction = (action: string) => {
  const userType = client.getCurrentUserType();
  
  if (userType === UserTypeEnum.VISITOR) {
    showUpgradePrompt({
      action,
      message: getContextualMessage(action),
      benefits: [
        'Join the conversation',
        'Create and share content',
        'Connect with community members',
        'Get personalized notifications'
      ]
    });
  }
};

const getContextualMessage = (action: string) => {
  const messages = {
    'react': 'Sign up to react and engage with posts',
    'comment': 'Create an account to join the discussion',
    'follow': 'Sign in to follow users and communities',
    'post': 'Become a member to share your thoughts'
  };
  return messages[action] || 'Sign up to unlock all features';
};

Troubleshooting

Symptoms: Cannot login as visitor, permission denied errorsSolutions:
  1. Verify visitor mode is enabled in your social.plus console
  2. Check API key has visitor access permissions
  3. Ensure you’re using correct region endpoint
  4. For secure mode, verify auth signature is correctly generated
  5. Check auth signature hasn’t expired
Symptoms: New device ID generated each session, losing visitor identitySolutions:
  1. Ensure device storage permissions are granted
  2. Check that SDK has access to persistent storage
  3. Verify cache/storage is not being cleared on app restart
  4. For web: check localStorage is enabled and accessible
  5. For mobile: verify keychain/SharedPreferences access
Symptoms: Visitors can perform restricted actions, permission errors not shownSolutions:
  1. Verify user type detection: client.getCurrentUserType()
  2. Check UIKit version supports visitor mode restrictions
  3. Ensure server-side permissions are properly configured
  4. Update to latest SDK version with visitor mode support
  5. Verify custom behavior overrides aren’t bypassing restrictions
Symptoms: Secure visitor login fails with invalid signature errorSolutions:
  1. Verify HMAC-SHA256 algorithm is used correctly
  2. Check message format: deviceId=${deviceId}&authSignatureExpiresAt=${authSignatureExpiresAt}
  3. Ensure application secret matches your backend configuration
  4. Verify timestamp format is ISO 8601
  5. Check signature is hex-encoded, not base64
  6. Ensure no extra whitespace in signature or timestamp
Symptoms: Newsfeed, notifications, or other pages visible to visitorsSolutions:
  1. Verify UIKit version supports visitor mode UI adaptations
  2. Check user type is correctly detected in UIKit
  3. Ensure config.json is properly loaded
  4. For clips feed: verify can_view_tab configuration
  5. Clear app cache and restart
  6. Check for custom navigation overrides

Next Steps

I