Spin-the-Wheel API
Phoenix provides a flexible spin-the-wheel gamification feature with configurable probability weights, frequency limits, date ranges, and visual themes.
Spin-the-Wheel API
Phoenix provides a flexible spin-the-wheel gamification feature that allows users to spin for rewards with configurable probability weights, frequency limits, and visual themes.
Overview
Spin-the-wheel lets you create wheel games where users can spin to win rewards. Each wheel has:
- Segments: Multiple segments with different reward items (or no-win segments)
- Probabilities: Weighted chances for each segment
- Frequency Limits: Configurable limits (daily, total, cooldown, or unlimited)
- Date Ranges: Optional start/end dates for time-limited wheels
- Themes: Visual styling configurations for the wheel appearance
Client Endpoints
All client endpoints require JWT authentication. Tenant is resolved from the JWT; paths do not include tenant_id.
Get Available Wheels
Retrieve all active wheels available for the tenant (from JWT).
GET /v1/wheelsResponse:
{
"wheels": [
{
"id": "a1b2c3d4-5678-90ab-cdef-1234567890ab",
"name": "Daily Spin",
"description": "Spin once per day for rewards",
"config": {
"segments": [
{
"reward_id": "550e8400-e29b-41d4-a716-446655440000",
"probability": 3,
"label": "100 Gems"
},
{
"reward_id": null,
"probability": 5,
"label": "Try Again"
},
{
"reward_id": "660e8400-e29b-41d4-a716-446655440001",
"probability": 2,
"label": "50 Coins"
}
],
"frequency": {
"type": "daily_limit",
"value": 1
},
"starts_at": "2025-01-01T00:00:00Z",
"ends_at": "2025-12-31T23:59:59Z",
"theme_id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
"theme_overrides": {
"wheel": {
"background_color": "#FF6B6B"
}
}
},
"resolved_theme": {
"wheel": {
"background_color": "#FF6B6B",
"border_color": "#4ECDC4",
"border_width": 3
},
"segments": {
"default_color": "#FFFFFF",
"text_color": "#000000"
}
}
}
]
}Theme Information:
theme_id- Optional reference to a theme configurationtheme_overrides- Optional partial theme overrides for this specific wheelresolved_theme- The final resolved theme configuration (base theme + overrides)
Spin the Wheel
Spin a wheel for a user. The result is determined by weighted random selection.
POST /v1/wheels/{wheel_id}/spinRequest Body:
{
"user_id": "user_123"
}Response:
{
"segment_index": 0,
"segment": {
"reward_item_id": "item-gems-100",
"probability": 3,
"label": "100 Gems"
},
"reward_item": {
"item_id": "item-gems-100",
"name": "100 Gems",
"description": "100 premium gems",
"reward_type": "currency",
"payload": {
"currency": "gems",
"amount": 100
}
}
}Error Responses:
400- Wheel not active, expired, not started, or frequency limit exceeded404- Wheel not found
Get User Spin History
Retrieve a user's spin history.
GET /v1/wheels/user/{user_id}/historyQuery Parameters:
| Parameter | Type | Description |
|---|---|---|
wheel_id | UUID | Filter by specific wheel (optional) |
limit | integer | Page size (default: 100, max: 1000) |
offset | integer | Page offset (default: 0) |
Response:
{
"spins": [
{
"id": "spin-123",
"wheel_id": "wheel-abc",
"result_index": 0,
"reward_item_id": "item-gems-100",
"spun_at": "2025-01-04T10:30:00Z"
},
{
"id": "spin-124",
"wheel_id": "wheel-abc",
"result_index": 1,
"reward_item_id": null,
"spun_at": "2025-01-03T15:45:00Z"
}
],
"total": 25
}JavaScript Example
// Get available wheels (tenant from JWT)
async function getAvailableWheels() {
const response = await fetch(
`${QUERY_API_URL}/v1/wheels`,
{
headers: {
'Authorization': `Bearer ${jwt}`,
'Content-Type': 'application/json'
}
}
);
return response.json();
}
// Spin a wheel
async function spinWheel(wheelId, userId) {
const response = await fetch(
`${QUERY_API_URL}/v1/wheels/${wheelId}/spin`,
{
method: 'POST',
headers: {
'Authorization': `Bearer ${jwt}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({ user_id: userId })
}
);
if (!response.ok) {
const error = await response.json();
throw new Error(error.error || 'Failed to spin wheel');
}
return response.json();
}
// Example: Display wheel and handle spin
const { wheels } = await getAvailableWheels();
const dailyWheel = wheels[0];
const result = await spinWheel(dailyWheel.id, 'user_123');
if (result.reward_item) {
console.log(`You won: ${result.reward_item.name}!`);
} else {
console.log('Better luck next time!');
}Wheel Configuration
Segments
Each segment represents one slice of the wheel:
{
"reward_id": "uuid-or-null", // null = no-win segment (UUID from rewards catalog)
"probability": 3, // Weight (higher = more likely)
"label": "100 Gems" // Optional display label
}Note: Segments now use reward_id which references rewards from the rewards catalog, not reward_item_id.
Probability Calculation:
- Probabilities are relative weights, not percentages
- Example:
[3, 5, 2]means segment 2 is 5/10 = 50% likely, segment 1 is 3/10 = 30%, segment 3 is 2/10 = 20%
Frequency Limits
Control how often users can spin:
| Type | Description | Value Meaning |
|---|---|---|
unlimited | No limits | value ignored |
daily_limit | Max spins per day | value = max spins (resets at midnight UTC) |
total_limit | Max spins ever | value = max spins per user |
cooldown | Time between spins | value = hours to wait |
Example Configurations:
// Daily limit: 3 spins per day
{
"type": "daily_limit",
"value": 3
}
// Total limit: 10 spins ever
{
"type": "total_limit",
"value": 10
}
// Cooldown: 4 hours between spins
{
"type": "cooldown",
"value": 4
}
// Unlimited
{
"type": "unlimited"
}Date Ranges
Optional time-based availability:
{
"starts_at": "2025-01-01T00:00:00Z", // Wheel not available before this
"ends_at": "2025-12-31T23:59:59Z" // Wheel not available after this
}Themes
Wheels can use visual themes to customize their appearance. Themes define colors, styling, and visual properties for the wheel and its segments.
Creating Themes
Create reusable theme configurations that can be applied to multiple wheels.
Endpoint:
POST /v1/wheel-themes?tenant_id=${tenant_id}Request:
{
"name": "Modern Blue Theme",
"description": "Clean blue color scheme",
"theme_type": "custom",
"config": {
"wheel": {
"background_color": "#4ECDC4",
"border_color": "#45B7B8",
"border_width": 3,
"center_color": "#FFFFFF",
"center_radius": 50
},
"segments": {
"default_color": "#FFFFFF",
"text_color": "#000000",
"text_font_size": 16,
"border_color": "#E0E0E0",
"border_width": 1
},
"segment_overrides": {
"0": {
"color": "#FFD700",
"text_color": "#000000"
},
"2": {
"color": "#FF6B6B",
"text_color": "#FFFFFF"
}
}
}
}Using Themes with Wheels
When creating a wheel, you can reference a theme:
{
"name": "Daily Spin",
"description": "Spin once per day",
"config": {
"segments": [
{ "reward_id": "550e8400-e29b-41d4-a716-446655440000", "probability": 3, "label": "100 Gems" },
{ "reward_id": null, "probability": 5, "label": "Try Again" }
],
"frequency": {
"type": "daily_limit",
"value": 1
},
"theme_id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
"theme_overrides": {
"wheel": {
"background_color": "#FF6B6B"
}
}
}
}Theme Overrides:
theme_id- Reference to a theme configurationtheme_overrides- Optional partial overrides that merge with the base theme- Only specified fields in
theme_overrideswill override the base theme
Listing Themes
Get all themes for a tenant:
GET /v1/wheel-themes?tenant_id=${tenant_id}Query Parameters:
theme_type- Filter by type (templateorcustom)status- Filter by status (activeorarchived)
System Templates
Phoenix provides system templates you can use as starting points:
GET /v1/wheel-themes/templatesThese templates are pre-configured themes you can reference or customize.
Admin Endpoints
Admins can manage wheel definitions via the admin API.
Create Wheel
POST /v1/wheels?tenant_id=${tenant_id}List Wheels
GET /v1/wheels?tenant_id=${tenant_id}Update Wheel
PUT /v1/wheels/{wheel_id}?tenant_id=${tenant_id}Delete Wheel
DELETE /v1/wheels/{wheel_id}?tenant_id=${tenant_id}Reward Tracking
Winning spins create orders that appear in the user's order history. Use the Query Gateway orders API (see Reward Grants):
GET /v1/orders/user/{user_id}
Authorization: Bearer <JWT>Filter or display orders from source_type or metadata to show wheel wins.
Best Practices
- Probability Design: Use simple ratios (e.g., 1, 2, 3) rather than percentages for easier adjustment
- No-Win Segments: Include segments with
reward_id: nullto create "try again" experiences - Frequency Limits: Start with daily limits to encourage daily engagement
- Date Ranges: Use for seasonal or promotional wheels
- Themes: Create reusable themes for consistent branding across multiple wheels
- Theme Overrides: Use theme overrides for wheel-specific customizations while maintaining base theme consistency
- Error Handling: Always handle frequency limit errors gracefully in your UI
Querying Coupons
How users can view their coupons, validate coupon codes, and redeem them. Covers listing definitions, user coupons, validation, redemption, and history.
Creating Spin the Wheel
This guide walks through creating and updating Spin-the-Wheel events through the Admin Gateway. Full setup documentation is in progress.