Profiles
Querying Profiles
How to retrieve user profile information. Profiles are automatically created if they don't exist.
Querying Profiles
This guide explains how to retrieve user profile information.
Overview
The Query API provides a simple endpoint to get user profile information. Profiles are automatically created if they don't exist, so you can always query them.
Get User Profile
Get a user's profile information. If the profile doesn't exist, it will be created automatically.
Endpoint:
GET /v1/profiles/user/{user_id}PUT same path to update profile (e.g. name, avatar).
Example Request:
GET /v1/profiles/user/user_123Response:
{
"tenant_id": "your_tenant",
"user_id": "user_123",
"name": "Brave Tiger 42",
"avatar_url": "https://api.dicebear.com/9.x/adventurer/svg?seed=your_tenant_user_123",
"created_at": "2026-01-28T10:00:00Z",
"updated_at": "2026-01-28T10:00:00Z"
}Profile Response Fields
| Field | Type | Description |
|---|---|---|
tenant_id | string | Tenant identifier |
user_id | string | User identifier |
name | string | Display name (auto-generated if not set) |
avatar_url | string | Avatar image URL (auto-generated) |
created_at | datetime | When profile was first created |
updated_at | datetime | Last update timestamp |
Automatic Profile Creation
If a profile doesn't exist when queried:
- A random name is generated (e.g., "Brave Tiger 42")
- An avatar URL is created based on tenant ID and user ID
- The profile is created and returned immediately
This means you can always query a profile - it will exist after the first request.
JavaScript Example
class ProfileClient {
constructor(apiUrl, jwt) {
this.apiUrl = apiUrl;
this.jwt = jwt;
}
async getProfile(userId) {
const response = await fetch(
`${this.apiUrl}/v1/profiles/user/${userId}`,
{
headers: {
'Authorization': `Bearer ${this.jwt}`,
'Content-Type': 'application/json'
}
}
);
if (!response.ok) {
throw new Error('Failed to fetch profile');
}
return response.json();
}
async getMultipleProfiles(userIds) {
// Fetch profiles in parallel (tenant from JWT)
const promises = userIds.map(userId =>
this.getProfile(userId).catch(err => {
console.error(`Failed to fetch profile for ${userId}:`, err);
return null;
})
);
const profiles = await Promise.all(promises);
return profiles.filter(p => p !== null);
}
}
// Example usage
const profile = new ProfileClient('https://query.phoenix.example.com', userJwt);
// Get single profile (tenant from JWT)
const userProfile = await profile.getProfile('user_123');
console.log(`User: ${userProfile.name}`);
console.log(`Avatar: ${userProfile.avatar_url}`);
// Display profile in UI
document.getElementById('user-name').textContent = userProfile.name;
document.getElementById('user-avatar').src = userProfile.avatar_url;
// Get multiple profiles (e.g., for leaderboard)
const userIds = ['user_123', 'user_456', 'user_789'];
const profiles = await profile.getMultipleProfiles(userIds);
profiles.forEach(p => {
console.log(`${p.user_id}: ${p.name}`);
});Using Profiles in Your Application
Display User Info
async function displayUserProfile(userId) {
const profile = await profileClient.getProfile(userId);
// Update UI elements
document.getElementById('user-name').textContent = profile.name;
document.getElementById('user-avatar').src = profile.avatar_url;
document.getElementById('user-avatar').alt = profile.name;
}Leaderboard Display
async function displayLeaderboard(tenantId, leaderboard) {
// Get all user IDs from leaderboard
const userIds = leaderboard.entries.map(entry => entry.user_id);
// Fetch all profiles
const profiles = await profileClient.getMultipleProfiles(userIds);
// Create a map for quick lookup
const profileMap = new Map(profiles.map(p => [p.user_id, p]));
// Display leaderboard with profile info
leaderboard.entries.forEach(entry => {
const userProfile = profileMap.get(entry.user_id);
console.log(`${entry.rank}. ${userProfile?.name || 'Unknown'}: ${entry.score}`);
});
}Avatar Caching
Since avatars are consistent (same seed = same avatar), you can cache them:
const avatarCache = new Map();
function getAvatarUrl(tenantId, userId) {
const key = `${tenantId}_${userId}`;
if (avatarCache.has(key)) {
return avatarCache.get(key);
}
// Avatar URL is deterministic based on tenant and user ID
const avatarUrl = `https://api.dicebear.com/9.x/adventurer/svg?seed=${tenantId}_${userId}`;
avatarCache.set(key, avatarUrl);
return avatarUrl;
}Best Practices
- Cache Profiles: Cache profile data to reduce API calls
- Batch Requests: When displaying multiple users (like in leaderboards), fetch profiles in parallel
- Handle Missing Profiles: Even though profiles auto-create, handle errors gracefully
- Avatar Optimization: Consider caching or preloading avatars for better performance
- Fallback Display: Show user ID or placeholder if profile fails to load
Error Handling
async function getProfileSafely(tenantId, userId) {
try {
const profile = await profileClient.getProfile(userId);
return profile;
} catch (error) {
console.error('Failed to fetch profile:', error);
// Return fallback profile
return {
tenant_id: tenantId,
user_id: userId,
name: `User ${userId}`,
avatar_url: getAvatarUrl(tenantId, userId),
created_at: new Date().toISOString(),
updated_at: new Date().toISOString()
};
}
}Next Steps
- Creating Profiles - Learn how to create and update profiles
- Profile Overview - Understand the profile system