Skip to main content

Player Onboarding

Learn about player registration strategies and how to integrate player onboarding into your application.

Overview

Player onboarding is the process of registering end-users in the UG Labs platform. This guide covers different registration strategies and provides complete implementation examples.

Prerequisites

Before implementing player onboarding, ensure you understand the Authentication flow and have created the necessary API keys.

Registration Strategies

Choose a registration strategy based on your architecture:

Open Registration

No Server Required - For Prototyping

This approach is perfect for quick prototyping and testing without backend infrastructure. Not recommended for production applications.

Embed playerCreate service account key in application:

Pros:

  • No backend required
  • Simpler architecture
  • Faster development
  • Quick prototyping

Cons:

  • Less control over registration
  • Service account key in client code
  • Limited validation options
  • Not recommended for production

Implementation:

// Client-side
const register = async (externalId) => {
// Create player directly from client
const response = await fetch('https://pug.stg.uglabs.app/api/players', {
method: 'POST',
headers: {
'Authorization': `Bearer ${PLAYER_CREATE_API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({ external_id: externalId })
});

const { federated_id } = await response.json();

// Store locally
localStorage.setItem('federatedId', federated_id);

return federated_id;
};
Open Registration Security

The playerCreate API key allows anyone to create players. Consider rate limiting and validation at the API level if using this approach.

Server Required - Recommended for Production

This approach provides full control and security. Best practice for production applications.

Your backend controls player creation:

Pros:

  • Full control over registration flow
  • Can implement custom validation
  • Federated IDs never exposed to client
  • Most secure option
  • Production-ready

Cons:

  • Requires backend infrastructure

Implementation:

// Backend endpoint
app.post('/register', async (req, res) => {
const { email } = req.body;

// 1. Validate user (custom logic)
if (!isValidEmail(email)) {
return res.status(400).json({ error: 'Invalid email' });
}

// 2. Create player in UG Labs
const federatedId = await createPlayer(email);

// 3. Store in your database
await db.users.create({ email, federatedId });

// 4. Return to client
res.json({ success: true, federatedId });
});

Complete Integration Example

Here's a complete example using the recommended managed registration:

Backend (Node.js/Express)

import express from 'express';

const app = express();

// Create player endpoint
app.post('/api/register', async (req, res) => {
const { email } = req.body;

try {
// Create player in UG Labs
const createResponse = await fetch('https://pug.stg.uglabs.app/api/players', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.UG_PRIVATE_API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({ external_id: email })
});

const { federated_id } = await createResponse.json();

// Store in your database
await database.users.create({
email,
federatedId: federated_id
});

res.json({ success: true });
} catch (error) {
res.status(500).json({ error: error.message });
}
});

// Login endpoint
app.post('/api/login', async (req, res) => {
const { email } = req.body;

// Get user from database
const user = await database.users.findOne({ email });

if (!user) {
return res.status(404).json({ error: 'User not found' });
}

// Authenticate with UG Labs
const authResponse = await fetch('https://pug.stg.uglabs.app/api/auth/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
api_key: process.env.UG_PUBLIC_API_KEY,
federated_id: user.federatedId
})
});

const { access_token } = await authResponse.json();

res.json({ access_token });
});

Frontend (React)

import { ConversationManager } from 'ug-js-sdk';
import { useState } from 'react';

const ChatApp = () => {
const [conversation, setConversation] = useState(null);

const handleLogin = async (email) => {
// Get access token from your backend
const response = await fetch('/api/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email })
});

const { access_token } = await response.json();

// Initialize conversation
const conv = new ConversationManager({
accessToken: access_token
});

await conv.start();
setConversation(conv);
};

const handleRegister = async (email) => {
// Register through your backend
const response = await fetch('/api/register', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email })
});

if (response.ok) {
// After registration, log them in
await handleLogin(email);
}
};

return (
<div>
{!conversation ? (
<>
<LoginForm onLogin={handleLogin} />
<RegisterForm onRegister={handleRegister} />
</>
) : (
<ChatInterface conversation={conversation} />
)}
</div>
);
};

Handling Duplicate Registrations

When a player with the same external_id already exists:

app.post('/api/register', async (req, res) => {
const { email } = req.body;

try {
// Check if player already exists in your database
const existingUser = await database.users.findOne({ email });

if (existingUser) {
return res.status(409).json({
error: 'User already exists',
shouldLogin: true
});
}

// Create new player in UG Labs
const createResponse = await fetch('https://pug.stg.uglabs.app/api/players', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.UG_PRIVATE_API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({ external_id: email })
});

// Handle UG Labs duplicate error
if (createResponse.status === 409) {
// Player exists in UG Labs but not in your DB
// This shouldn't normally happen, but handle it gracefully
return res.status(500).json({
error: 'Data synchronization error. Please contact support.'
});
}

const { federated_id } = await createResponse.json();

// Store in your database
await database.users.create({
email,
federatedId: federated_id
});

res.json({ success: true });
} catch (error) {
res.status(500).json({ error: error.message });
}
});

Migration from Existing Systems

If you have existing users, you can bulk-create players:

// Migration script
const migrateExistingUsers = async () => {
const users = await database.users.findAll();

for (const user of users) {
try {
// Create player in UG Labs
const response = await fetch('https://pug.stg.uglabs.app/api/players', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.UG_PRIVATE_API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
external_id: user.email
})
});

const { federated_id } = await response.json();

// Update user record with federated_id
await database.users.update(
{ federatedId: federated_id },
{ where: { id: user.id } }
);

console.log(`Migrated user: ${user.email}`);
} catch (error) {
console.error(`Failed to migrate ${user.email}:`, error);
}
}
};

Best Practices

User Experience

  1. Provide clear feedback during registration and login
  2. Handle errors gracefully with user-friendly messages
  3. Implement loading states for async operations
  4. Validate inputs on both client and server
  5. Use progressive disclosure for complex registration forms

Security

  1. Validate all inputs before creating players
  2. Rate limit registration endpoints to prevent abuse
  3. Log registration attempts for security monitoring
  4. Use HTTPS only for all authentication requests
  5. Sanitize external IDs to prevent injection attacks

Data Management

  1. Store federated IDs securely in your database
  2. Index external_id fields for fast lookups
  3. Backup user data regularly including federated IDs
  4. Handle database failures gracefully
  5. Implement proper error recovery mechanisms

Common Patterns

Email/Password Registration

app.post('/api/register', async (req, res) => {
const { email, password } = req.body;

// 1. Validate email and password
if (!isValidEmail(email) || !isValidPassword(password)) {
return res.status(400).json({ error: 'Invalid email or password' });
}

// 2. Hash password
const hashedPassword = await bcrypt.hash(password, 10);

// 3. Create player in UG Labs
const createResponse = await fetch('https://pug.stg.uglabs.app/api/players', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.UG_PRIVATE_API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({ external_id: email })
});

const { federated_id } = await createResponse.json();

// 4. Store in database
await database.users.create({
email,
password: hashedPassword,
federatedId: federated_id
});

res.json({ success: true });
});

Social Login (OAuth)

app.post('/api/auth/google/callback', async (req, res) => {
const { googleToken } = req.body;

// 1. Verify Google token
const googleUser = await verifyGoogleToken(googleToken);
const email = googleUser.email;

// 2. Check if user exists
let user = await database.users.findOne({ email });

if (!user) {
// 3. Create new player
const createResponse = await fetch('https://pug.stg.uglabs.app/api/players', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.UG_PRIVATE_API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({ external_id: email })
});

const { federated_id } = await createResponse.json();

user = await database.users.create({
email,
federatedId: federated_id,
authProvider: 'google'
});
}

// 4. Authenticate with UG Labs
const authResponse = await fetch('https://pug.stg.uglabs.app/api/auth/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
api_key: process.env.UG_PUBLIC_API_KEY,
federated_id: user.federatedId
})
});

const { access_token } = await authResponse.json();

res.json({ access_token });
});

Anonymous Users (Device ID)

// Client-side
const registerAnonymous = async () => {
// Generate or retrieve device ID
let deviceId = localStorage.getItem('deviceId');

if (!deviceId) {
deviceId = crypto.randomUUID();
localStorage.setItem('deviceId', deviceId);
}

// Register with your backend
const response = await fetch('/api/register-anonymous', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ deviceId })
});

const { access_token } = await response.json();
return access_token;
};

// Backend
app.post('/api/register-anonymous', async (req, res) => {
const { deviceId } = req.body;

// Check if device already registered
let user = await database.users.findOne({ deviceId });

if (!user) {
// Create new player
const createResponse = await fetch('https://pug.stg.uglabs.app/api/players', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.UG_PRIVATE_API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({ external_id: `device_${deviceId}` })
});

const { federated_id } = await createResponse.json();

user = await database.users.create({
deviceId,
federatedId: federated_id,
isAnonymous: true
});
}

// Authenticate
const authResponse = await fetch('https://pug.stg.uglabs.app/api/auth/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
api_key: process.env.UG_PUBLIC_API_KEY,
federated_id: user.federatedId
})
});

const { access_token } = await authResponse.json();
res.json({ access_token });
});

Next Steps