User Guide¶
This guide covers the SDK features in detail.
Working with Members¶
Members are players with full profile access. When you fetch a member, you automatically subscribe to their rating updates.
member = await client.get_member("clerk_user_123")
# Access properties
print(member.name) # "John Smith"
print(member.rating) # 4.25
print(member.is_vairified) # True
# Refresh data from API
await member.refresh()
Rating Splits¶
Ratings are broken down by category:
splits = member.rating_splits
# Convenience properties
splits.open # Open division rating
splits.gender # Same-gender doubles
splits.mixed # Mixed doubles
splits.recreational # Recreational
splits.singles # Singles
splits.best # Best available rating
# Age brackets
splits.get("50_and_up")
splits.get("60_and_up")
Searching Players¶
The search endpoint supports extensive filtering:
results = await client.search(
name="John", # Partial name match
city="Austin",
state="TX",
country="US",
zip_code="78701",
rating_min=3.5,
rating_max=4.5,
gender="MALE",
vairified_only=True,
age=35, # Exact age
# Or age range:
# age_min=30,
# age_max=40,
sort_by="rating",
sort_order="desc",
limit=20,
)
Pagination¶
Search results support pagination:
results = await client.search(city="Austin", limit=20)
print(f"Page {results.page} of {results.pages}")
print(f"Total: {results.total} players")
# Iterate current page
for player in results:
print(player.name)
# Get next page
if results.has_more:
next_results = await results.next_page()
Find Player Helper¶
For simple name lookups:
player = await client.find_player("John Smith")
if player:
print(f"Found: {player.name} ({player.rating})")
Submitting Matches¶
Match Structure¶
Matches require event information, teams, and scores:
from datetime import datetime
from vairified import Match
# Doubles match
match = Match(
event="Weekly League", # Required
bracket="4.0 Doubles", # Required
date=datetime.now(), # Required
team1=("player1", "player2"), # Player IDs
team2=("player3", "player4"),
scores=[(11, 9), (11, 7)], # (team1_score, team2_score) per game
location="Austin Club", # Optional
match_type="SIDEOUT", # Default: "SIDEOUT"
source="PARTNER", # Default: "PARTNER"
)
# Singles match
singles = Match(
event="Club Singles",
bracket="Open Singles",
date=datetime.now(),
team1=("player1",), # Single player tuple
team2=("player2",),
scores=[(11, 8), (9, 11), (11, 6)],
)
Match Properties¶
match.format # "DOUBLES" or "SINGLES"
match.winner # 1, 2, or 0 (tie)
match.score_summary # "11-9, 11-7"
match.identifier # Auto-generated unique ID
Submitting¶
Submit single or batch:
# Single match
result = await client.submit_match(match)
# Batch submission
result = await client.submit_matches([match1, match2, match3])
# Check result
if result:
print(f"Success: {result.num_games} games recorded")
else:
print(f"Errors: {result.errors}")
Match Result¶
The MatchResult object contains:
result.success # True/False
result.num_matches # Matches processed
result.num_games # Games recorded
result.dry_run # True if validation only
result.message # Human-readable message
result.errors # List of errors
# Truthiness check
if result: # True if success and no errors
print("OK!")
Dry-Run Mode¶
API keys with the dry-run scope validate without persisting data:
result = await client.submit_matches([match1, match2])
if result.dry_run:
print("Dry-run mode - no data saved")
print(f"Would create: {result.num_games} games")
print(result.message)
Request a dry-run API key from your Vairified partner contact.
Rating Updates¶
Poll for rating changes on subscribed members:
# Subscribe by fetching members
await client.get_member("user_1")
await client.get_member("user_2")
# Later, check for updates
updates = await client.get_rating_updates()
for update in updates:
print(f"{update.member_id}: {update.previous_rating:.2f} → {update.new_rating:.2f}")
if update.improved:
member = await update.get_member()
print(f"{member.name} improved!")
Webhook Testing¶
Test your webhook endpoint:
result = await client.test_webhook("https://your-service.com/webhook")
Advanced Configuration¶
Custom Base URL¶
client = Vairified(
api_key="vair_pk_xxx",
base_url="https://custom-api.example.com/api/v1",
timeout=60.0,
)
Without Context Manager¶
If you need manual lifecycle control:
client = Vairified(api_key="vair_pk_xxx")
try:
member = await client.get_member("user_123")
finally:
await client.close()
OAuth Connect Flow¶
The Partner API uses OAuth to request player consent before accessing their data. Players must explicitly approve your application before you can access their profile.
Available OAuth Scopes¶
Exchanging Tokens¶
After the user approves, they’re redirected to your callback URL with a code:
# Handle callback: https://your-app.com/callback?code=xxx&state=yyy
# Verify state matches what you stored
if request.query_params["state"] != stored_state:
raise ValueError("Invalid state - possible CSRF attack")
# Exchange code for tokens
tokens = await client.exchange_token(
code=request.query_params["code"],
redirect_uri="https://your-app.com/callback",
)
# Store tokens securely
player_id = tokens.player_id
access_token = tokens.access_token
refresh_token = tokens.refresh_token
# Now access the player's data
member = await client.get_member(player_id)
Refreshing Tokens¶
Access tokens expire. Use the refresh token to get a new one:
from vairified import OAuthError
try:
new_tokens = await client.refresh_access_token(stored_refresh_token)
# Update stored tokens
except OAuthError as e:
if e.error_code == "invalid_grant":
# Refresh token revoked - user needs to re-authorize
pass
Revoking Connections¶
Disconnect a player from your application:
await client.revoke_connection("vair_mem_xxx")
# Player is now disconnected
API Usage Statistics¶
Check your API usage:
usage = await client.get_usage()
print(f"Requests today: {usage['requestsToday']}")
print(f"Rate limit: {usage['rateLimit']}/hour")