The Loyalty Portal is a fan engagement system built around quests, points, and rewards. Fans complete challenges (quests) to earn points. As they accumulate points, they reach milestone tiers and unlock rewards — badges, access passes, promo codes, and more.
This guide covers the key concepts, setup process, and API integration for building a custom application on top of the Loyalty Portal.
Key Concepts
Account — Your Tradable Bits account is the top-level container. API access is authenticated
using your account's api_key and api_secret.
Business (Team) — A Business represents a team or brand within your account. The loyalty portal is configured independently for each business — each has its own quests, rewards, tiers, and branding.
Fan — An end-user who interacts with the loyalty portal. Fans register with an email address and authenticate via password, email verification code, or SMS code. Each fan accumulates their own point balance and quest completions.
Point Type — The loyalty currency for a business (e.g., "Fan Points", "Loyalty XP"). You create a point type once and assign it to the business. All quests, tiers, and rewards reference this currency.
Performance (Event) — Represents a scheduled event, game, or show. Quests and rewards can be tied to a specific performance so they only appear during that event's timeframe.
Fan Point — A transaction record indicating the award of points or a reward. Each fan point entry represents a single point change — positive for points earned (e.g., completing a quest) or a reward delivery record. A fan's balance is the sum of all their fan point transactions.
Fan Activity — Interactions with the Loyalty Portal are based on fan activities — a record indicating a login or some fact of interaction with the portal. If the API is used to present the portal, these records must be created programmatically as shown in the runtime flow below. A fan must have at least one activity record to be eligible for automatic reward delivery.
Important: Fan Points and Fan Activities are required for the Loyalty Portal to function.
Fan Points are the transaction engine behind the loyalty currency and reward delivery. Every point earned, spent, or reward assigned is recorded as a fan point transaction. Without these records, tiers cannot be calculated, quest rewards cannot be tracked, and reward eligibility cannot be determined.
Fan Activities are required for automatic reward processing. The background reward system
only considers fans who have at least one activity record associated with the business. When
building a custom integration via API, your application must ensure fan activities are created
when fans interact with the portal (e.g., when completing quests via the
POST /api/v1/ent/businesses/{business_id}/quests/{page_tab_id}/fans/{fan_id}/complete
endpoint, which creates the activity automatically). Without activity records, fans will not
receive rewards even if they meet all point tier or quest requirements.
Point Tiers
Point Tiers are named milestones within a point type. They define the progression levels fans can reach. A fan's current tier is the highest tier whose minimum points they have reached.
Tiers serve two purposes:
- Gate quest access — fans must reach a tier to unlock certain quests
- Trigger automatic rewards — fans receive rewards when they reach a tier threshold
| Tier Example | Minimum Points |
|---|---|
| Bronze | 100 |
| Silver | 500 |
| Gold | 1,500 |
| Platinum | 5,000 |
Quests
A Quest is a challenge that fans complete to earn points. Each quest awards a configurable number of points on completion. Quests can be global (always available) or tied to a specific performance (event/game).
There are six quest types:
| Quest Type | What the fan does |
|---|---|
campaign | Complete a Tradable Bits campaign (contest, poll, survey, etc.) |
quickpicks | Answer prediction-style questions in a QuickPicks campaign |
link_url | Visit a specific tracked URL |
scan | Scan a QR code or NFC tag at a physical venue |
wristband | Link a wristband or ticket barcode to their account |
custom | A flexible quest completed through an API call from your app |
Access Restrictions — Quests can optionally be locked until the fan meets certain criteria:
| Restriction | What the fan needs |
|---|---|
| (none) | Open to everyone |
fan_points | Fan must have accumulated a minimum number of total points |
point_tier | Fan must have reached a specific tier |
quests | Fan must have completed one or more prerequisite quests |
tag | Fan must have a specific CRM tag (quest is hidden otherwise) |
This lets you build progressive quest chains — complete Quest A to unlock Quest B, reach Gold tier to unlock Quest C, etc.
Rewards
A Reward is something a fan receives when they meet specific requirements. Every reward has an expiration date and an optional limit on how many fans can receive it.
Requirement types — How a reward is triggered:
| Requirement | How the fan earns it |
|---|---|
point_tier | Automatically awarded when the fan's total points reach the linked tier's threshold |
quests | Automatically awarded when the fan completes all quests in a required set |
| (none) | Assigned manually through the API or admin UI |
Delivery types — What the fan receives:
| Reward Type | Description |
|---|---|
badge | A digital badge displayed on the fan's profile |
pass | A time-limited access pass presented as a QR code |
prize_code | A redeemable code drawn from a pre-loaded pool |
atvenu | A promotion pushed to a wristband or QR code via AtVenu |
bestring | A promotion pushed to a wristband via BestRing |
Rewards are processed automatically by a background task. When a fan meets a reward's requirements, the system delivers the reward and optionally sends an SMS and/or email notification.
Fans must have logged into the loyalty portal at least once to be eligible for automatic rewards.
Setup Process
Follow these steps to configure a loyalty portal for a business.
Step 1: Create a Point Type
Create a loyalty currency for the business. This is done in the admin UI under CRM > Points, or via the API:
import requests
data = {"api_key": "YOUR_API_KEY", "api_secret": "YOUR_API_SECRET",
"point_name": "Fan Points"}
res = requests.post("https://nest.tradablebits.com/api/v1/crm/points", data=data)
result = res.json()
print("point_uid:", result["point_uid"])
Step 2: Assign Point Type to Business
Link the point type to the business. In the admin UI, go to Business > Fan Loyalty Portal and select the point type. This page also configures portal branding — colors, fonts, header images, intro pages, and notification preferences.
Step 3: Create Point Tiers
Define milestone levels within the point type. In the admin UI, go to CRM > Point Tiers, or via the API:
import requests
point_uid = "YOUR_POINT_UID"
data = {"api_key": "YOUR_API_KEY", "api_secret": "YOUR_API_SECRET",
"tier_name": "Bronze", "min_points": 100}
res = requests.post(f"https://nest.tradablebits.com/api/v1/crm/point_tiers/{point_uid}", data=data)
result = res.json()
print("point_tier_uid:", result["point_tier_uid"])
Step 4: Create Quests
Build the challenges fans will complete. In the admin UI, go to Quests under the business, or via the API:
import requests
business_id = 12345
data = {"api_key": "YOUR_API_KEY", "api_secret": "YOUR_API_SECRET",
"quest_type": "custom",
"quest_title": "Complete your profile",
"quest_description": "Fill in all your profile details",
"reward_type": "fan_points",
"fan_points_reward": 50}
res = requests.post(f"https://nest.tradablebits.com/api/v1/ent/businesses/{business_id}/quests", data=data)
result = res.json()
print("page_tab_id:", result["page_tab_id"])
Quest creation parameters:
| Field | Type | Required | Description |
|---|---|---|---|
quest_type | string | Yes | One of: campaign, quickpicks, link_url, scan, wristband, custom |
quest_title | string | Yes | Display title (max 128 chars) |
quest_description | string | No | Longer description text |
button_text | string | No | CTA button label (max 32 chars) |
reward_type | string | No | fan_points or null |
fan_points_reward | integer | Conditional | Points awarded on completion (required if reward_type is fan_points, max 10,000,000) |
performance_uid | uuid | Conditional | Tie quest to a specific event (required for scan and wristband) |
target_page_tab_id | integer | Conditional | Campaign ID for campaign or quickpicks quests |
link_url | string | Conditional | Destination URL for link_url quests (tracker auto-generated) |
scan_group_names | list | Conditional | Scan group names for scan quests |
site_name | string | Conditional | Site name for scan quests (max 128 chars) |
access_restriction_type | string | No | One of: fan_points, quests, point_tier, tag |
fan_points_required | integer | Conditional | Required when access_restriction_type is fan_points |
point_tier_uid | uuid | Conditional | Required when access_restriction_type is point_tier |
required_page_tab_ids | list[int] | Conditional | Required when access_restriction_type is quests |
tag_name | string | Conditional | Required when access_restriction_type is tag |
start_date | date | No | Quest availability start (ISO format) |
end_date | date | No | Quest availability end |
media_uid | uuid | No | Image asset for the quest |
idol_uid | uuid | No | Associate with a specific idol/artist |
Step 5: Create Rewards
Set up rewards fans will unlock. In the admin UI, go to Milestone Rewards under the business, or via the API:
import requests
business_id = 12345
data = {"api_key": "YOUR_API_KEY", "api_secret": "YOUR_API_SECRET",
"reward_title": "Bronze Badge",
"reward_type": "badge",
"requirement_type": "point_tier",
"point_tier_uid": "YOUR_TIER_UID",
"expiration_timestamp": "2026-12-31T23:59:59",
"timezone": "America/Los_Angeles",
"winner_selection_model": "all"}
res = requests.post(f"https://nest.tradablebits.com/api/v1/ent/businesses/{business_id}/rewards", data=data)
result = res.json()
print("reward_uid:", result["reward_uid"])
Reward creation parameters:
| Field | Type | Required | Description |
|---|---|---|---|
reward_title | string | Yes | Display title (must be unique per business) |
reward_type | string | Yes | One of: badge, pass, prize_code, atvenu, bestring |
expiration_timestamp | datetime | Yes | When the reward expires (ISO format) |
timezone | string | Yes | Timezone (e.g., America/Los_Angeles) |
winner_selection_model | string | Yes | all (unlimited) or first (limited supply) |
reward_limit | integer | Conditional | Max recipients (required when winner_selection_model is first) |
requirement_type | string | No | point_tier, quests, or null |
point_tier_uid | uuid | Conditional | Required when requirement_type is point_tier |
required_page_tab_ids | list[int] | Conditional | Required when requirement_type is quests |
description | string | No | Longer description text |
performance_uid | uuid | No | Tie to a specific event (required for atvenu and bestring) |
Reward-type-specific fields:
| Reward Type | Additional Fields |
|---|---|
pass | access_start_timestamp, access_end_timestamp, access_pass_type (e.g., qrcode) |
prize_code | prize_codes (comma or newline-separated list), is_limited (single-use flag) |
atvenu | atvenu_sales_event_key, atvenu_promo_key, atvenu_delivery_method (wristband or qrcode) |
bestring | bestring_location_group_id, bestring_promotion_id |
Authentication Flow
All fan-facing interactions require authentication. The flow uses the sessions API to obtain
a session_uid and fan_id for all subsequent calls.
Step 1: Check email — Determine if the fan exists
import requests
email = "fan@example.com"
data = {"api_key": "YOUR_API_KEY", "network": "email", "email": email}
res = requests.post("https://nest.tradablebits.com/api/v1/sessions/connect", data=data)
result = res.json()
if result["status"] == "register":
print("New fan — registration required")
elif result["status"] == "login":
print("Existing fan — login required")
has_password = result.get("password", False)
has_phone = result.get("phone", False)
Step 2: Register (if new fan)
data = {"api_key": "YOUR_API_KEY", "network": "register",
"email": email, "first_name": "Jane", "last_name": "Doe"}
res = requests.post("https://nest.tradablebits.com/api/v1/sessions/connect", data=data)
Step 3: Login — via password, email verification, or SMS verification
# Password login
data = {"api_key": "YOUR_API_KEY", "network": "email",
"email": email, "password": "fanpassword"}
res = requests.post("https://nest.tradablebits.com/api/v1/sessions/connect", data=data)
result = res.json()
session_uid = result["session_uid"]
fan_id = result["fan_id"]
# Email verification login (two-step)
# 1. Request verification code
data = {"api_key": "YOUR_API_KEY", "network": "verify_email", "email": email}
res = requests.post("https://nest.tradablebits.com/api/v1/sessions/connect", data=data)
request_uid = res.json()["request_uid"]
# 2. Submit the code the fan received
data = {"api_key": "YOUR_API_KEY", "network": "submit_verification_code",
"email": email, "verification_code": "49270", "request_uid": request_uid}
res = requests.post("https://nest.tradablebits.com/api/v1/sessions/connect", data=data)
session_uid = res.json()["session_uid"]
fan_id = res.json()["fan_id"]
Runtime API Flow
Once authentication is complete and the portal is configured, your application interacts with the loyalty portal through the following API sequence.
1. Get businesses
data = {"api_key": "YOUR_API_KEY"}
res = requests.get("https://nest.tradablebits.com/api/v1/businesses", params=data)
businesses = res.json()
for biz in businesses:
print(biz["business_id"], biz["business_name"])
2. Get point tiers for the business
point_uid = "BUSINESS_POINT_UID"
data = {"api_key": "YOUR_API_KEY", "api_secret": "YOUR_API_SECRET"}
res = requests.get(f"https://nest.tradablebits.com/api/v1/crm/point_tiers/{point_uid}", params=data)
tiers = res.json()
for tier in tiers:
print(tier["tier_name"], tier["min_points"])
3. Get the fan's current tier
data = {"api_key": "YOUR_API_KEY", "api_secret": "YOUR_API_SECRET",
"point_name": "Fan Points"}
res = requests.get(f"https://nest.tradablebits.com/api/v1/crm/fans/{fan_id}/point_tier", params=data)
fan_tier = res.json()
4. Get quests
business_id = 12345
data = {"api_key": "YOUR_API_KEY", "api_secret": "YOUR_API_SECRET"}
res = requests.get(f"https://nest.tradablebits.com/api/v1/ent/businesses/{business_id}/quests", params=data)
quests = res.json()
5. Get the fan's quest completions
data = {"api_key": "YOUR_API_KEY", "api_secret": "YOUR_API_SECRET"}
res = requests.get(f"https://nest.tradablebits.com/api/v1/ent/businesses/{business_id}/quests/fans/{fan_id}/complete", params=data)
completions = res.json() # { page_tab_id: true/false }
6. Complete a quest
page_tab_id = 67890
data = {"api_key": "YOUR_API_KEY", "api_secret": "YOUR_API_SECRET"}
res = requests.post(f"https://nest.tradablebits.com/api/v1/ent/businesses/{business_id}/quests/{page_tab_id}/fans/{fan_id}/complete", data=data)
result = res.json()
print("activity_id:", result["activity_id"])
Points are automatically awarded based on the quest's fan_points_reward configuration.
If the fan has already completed the quest, the existing activity_id is returned.
7. Get the fan's rewards
data = {"api_key": "YOUR_API_KEY", "api_secret": "YOUR_API_SECRET"}
res = requests.get(f"https://nest.tradablebits.com/api/v1/ent/businesses/{business_id}/fans/{fan_id}/rewards", params=data)
rewards = res.json()
8. Manually assign a reward (for rewards with no automatic requirement)
data = {"api_key": "YOUR_API_KEY", "api_secret": "YOUR_API_SECRET",
"reward_uid": "REWARD_UID"}
res = requests.post(f"https://nest.tradablebits.com/api/v1/ent/businesses/{business_id}/fans/{fan_id}/rewards", data=data)
9. Poll for recent reward updates
data = {"api_key": "YOUR_API_KEY", "api_secret": "YOUR_API_SECRET",
"last_update_date": "2026-01-01"}
res = requests.get("https://nest.tradablebits.com/api/v1/ent/reward_updates", params=data)
updates = res.json()
Determining Quest Status
After fetching quests and completions, your application should evaluate each quest's status for the fan:
- If the quest's
page_tab_idis in the completions map astrue— completed - If
access_restriction_typeis set, check the restriction:fan_points— compare the fan's total points againstfan_points_requiredpoint_tier— compare the fan's total points against the tier'smin_pointsquests— check if allrequired_page_tab_idsare completedtag— check if the fan has the required CRM tag (hide the quest if they don't)
- If restrictions are not met — locked (show with lock icon and explanation)
- Otherwise — active (available to complete)
API Reference Summary
| Action | Method | Endpoint |
|---|---|---|
| Authenticate fan | POST | /api/v1/sessions/connect |
| Get businesses | GET | /api/v1/businesses |
| List point types | GET | /api/v1/crm/points |
| Create point type | POST | /api/v1/crm/points |
| List point tiers | GET | /api/v1/crm/point_tiers/{point_uid} |
| Create/update tier | POST | /api/v1/crm/point_tiers/{point_uid} |
| Get fan's current tier | GET | /api/v1/crm/fans/{fan_id}/point_tier |
| List quests | GET | /api/v1/ent/businesses/{business_id}/quests |
| Create quest | POST | /api/v1/ent/businesses/{business_id}/quests |
| Delete quest | DELETE | /api/v1/ent/businesses/{business_id}/quests/{page_tab_id} |
| List rewards | GET | /api/v1/ent/businesses/{business_id}/rewards |
| Create reward | POST | /api/v1/ent/businesses/{business_id}/rewards |
| Delete reward | DELETE | /api/v1/ent/businesses/{business_id}/rewards/{reward_uid} |
| Get fan's rewards | GET | /api/v1/ent/businesses/{business_id}/fans/{fan_id}/rewards |
| Assign reward to fan | POST | /api/v1/ent/businesses/{business_id}/fans/{fan_id}/rewards |
| Get quest completions | GET | /api/v1/ent/businesses/{business_id}/quests/fans/{fan_id}/complete |
| Complete quest | POST | /api/v1/ent/businesses/{business_id}/quests/{page_tab_id}/fans/{fan_id}/complete |
| Poll reward updates | GET | /api/v1/ent/reward_updates |
| Render media asset | GET | /fb_media/{media_uid} |
Media assets referenced by media_uid can be rendered via https://nest.tradablebits.com/fb_media/{media_uid}.
Optional query parameters: width, height for image resizing.