QuickStart
BetSync Quickstart Guide
BetSync enables your users to securely connect their sportsbook accounts for automatic bet tracking, sharing, and analytics. This guide will walk you through setting up a complete BetSync integration.
Prerequisites
Before you begin, you'll need:
- A SharpSports API account (Sign up here)
- Your public API key (found in Dashboard → Settings → API Keys)
- A webhook endpoint URL for receiving events
- Basic understanding of REST APIs
Step 1: Set Up Your Development Environment
1.1 Start with Sandbox Mode
All new accounts start in sandbox mode, perfect for testing:
# Sandbox API endpoint
BASE_URL=https://api.sharpsports.io/v1/
# Your sandbox public API key
API_KEY=public_sandbox_XXXXXXXXX1.2 Test Your API Connection
curl -X GET \
-H "Authorization: Token YOUR_PUBLIC_API_KEY" \
https://api.sharpsports.io/v1/booksStep 2: Implement Account Linking (SDK Required)
2.1 Generate a Context ID with Internal ID (Required)
The internalId is a required field that links the SharpSports bettor to your internal user system. This ID:
- Must be unique for each user in your system
- Will be included in all webhook events for this user
- Allows you to associate bet data with your user records
- Cannot be changed once set for a bettor
Create a context for the linking session:
curl -X POST \
-H "Authorization: Token YOUR_PUBLIC_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"internalId": "user_123", # REQUIRED: Your unique user identifier
"webhookUrl": "https://yourapp.com/webhooks/sharpsports"
}' \
https://api.sharpsports.io/v1/contextResponse:
{
"cid": "CTX_abc123xyz",
"internalId": "user_123", // Your user ID linked to this session
"webhookUrl": "https://yourapp.com/webhooks/sharpsports"
}2.2 SDK Integration (Choose Your Platform)
Important: You must use one of the SharpSports SDKs to enable account linking. Choose the SDK that matches your platform:
Option A: Web Browser SDK
// Install the SDK
npm install @sharpsports/web-sdk
// Import and initialize
import { SharpSports } from '@sharpsports/web-sdk';
const ss = new SharpSports({
apiKey: 'YOUR_PUBLIC_API_KEY'
});
// Link account with required internalId
const result = await ss.linkAccount({
internalId: 'user_123', // REQUIRED: Your unique user ID
contextId: 'CTX_abc123xyz'
});
// Direct to linking UI
const linkUrl = `https://ui.sharpsports.io/link/${result.contextId}`;
window.open(linkUrl, 'LinkAccount', 'width=600,height=800');Option B: React Native SDK
// Install the SDK
npm install @sharpsports/react-native-sdk
// Import and use
import { linkAccount } from '@sharpsports/react-native-sdk';
const result = await linkAccount({
token: 'YOUR_PUBLIC_API_KEY',
internalId: 'user_123' // REQUIRED: Your unique user ID
});Option C: iOS SDK (Swift)
// Install via CocoaPods
pod 'SharpSportsSDK'
// Link account
SharpSports.linkAccount(
token: "YOUR_PUBLIC_API_KEY",
internalId: "user_123" // REQUIRED: Your unique user ID
) { result in
// Handle result
}Option D: Android SDK (Kotlin)
// Add to build.gradle
implementation 'io.sharpsports:android-sdk:latest'
// Link account
SharpSports.linkAccount(
token = "YOUR_PUBLIC_API_KEY",
internalId = "user_123" // REQUIRED: Your unique user ID
) { result ->
// Handle result
}Step 3: Handle Webhook Events
3.1 Set Up Your Webhook Endpoint
Create an endpoint to receive SharpSports webhooks:
// Express.js example
app.post('/webhooks/sharpsports', (req, res) => {
const { type, data } = req.body;
switch(type) {
case 'bettor.created':
handleBettorCreated(data);
break;
case 'bettorAccount.verified':
handleAccountVerified(data);
break;
case 'refreshResponse.created':
handleRefreshComplete(data);
break;
case 'bettorAccount.unverified':
handleAccountUnverified(data);
break;
}
// Always respond quickly with 200
res.status(200).send('OK');
// Process heavy operations asynchronously
processWebhookAsync(type, data);
});3.2 Critical Webhook Events
| Event | When It Fires | Action Required |
|---|---|---|
bettor.created | First account linked for user | Store bettorId with internalId |
bettorAccount.verified | Account successfully linked/verified | Update UI to show connected status |
refreshResponse.created | Bet data refresh completed | Process and store bet slips |
bettorAccount.unverified | Account needs re-authentication | Prompt user to re-link account |
Step 4: Refresh Bet Data
4.1 Manual Refresh (User-Initiated)
Important: All refreshes must be initiated manually via API calls. There is no automatic refresh cadence.
// Refresh all accounts for a bettor
async function refreshAllAccounts(bettorId) {
const response = await fetch(
`https://api.sharpsports.io/v1/bettors/${bettorId}/refresh`,
{
method: 'POST',
headers: {
'Authorization': `Token ${API_KEY}`
}
}
);
return response.json();
}
// Refresh specific account
async function refreshAccount(bettorAccountId) {
const response = await fetch(
`https://api.sharpsports.io/v1/bettorAccounts/${bettorAccountId}/refresh`,
{
method: 'POST',
headers: {
'Authorization': `Token ${API_KEY}`
}
}
);
return response.json();
}Best Practices for Refresh Timing:
- Trigger a refresh when user logs into your app
- Add a manual refresh button for users
- Limit refreshes to 1-2 times per day per account
- Handle refresh responses via webhooks (don't poll)
Step 5: Display Bet Data
5.1 Fetch Bet Slips
// Get all bet slips for a bettor
async function getBetSlips(bettorId) {
const response = await fetch(
`https://api.sharpsports.io/v1/betSlips?bettorId=${bettorId}`,
{
headers: {
'Authorization': `Token ${API_KEY}`
}
}
);
return response.json();
}5.2 Understanding Bet Structure
// Example bet slip structure
{
"id": "SLIP_xyz123",
"bettorAccountId": "BACT_abc456",
"status": "won", // pending, won, lost, push, canceled
"atRisk": 100.00,
"toWin": 191.00,
"payout": 291.00,
"placedAt": "2024-01-15T10:30:00Z",
"gradedAt": "2024-01-15T23:45:00Z",
"bets": [
{
"id": "BET_def789",
"type": "spread",
"odds": -110,
"bookDescription": "Lakers -5.5",
"incomplete": false,
"event": {
"id": "EVT_ghi012",
"sport": "basketball",
"league": "nba",
"startTime": "2024-01-15T19:00:00Z"
},
"marketSelection": {
"team": {
"id": "TEAM_lakers",
"name": "Los Angeles Lakers"
},
"position": "favorite",
"spread": -5.5
}
}
]
}5.3 Handle Incomplete Bets
When incomplete: true, only display accounting fields and bookDescription:
function displayBet(bet) {
if (bet.incomplete) {
// Show only what we have
return {
description: bet.bookDescription,
odds: bet.odds,
status: bet.status
};
}
// Full bet details available
return {
team: bet.marketSelection.team.name,
type: bet.type,
line: bet.marketSelection.spread,
odds: bet.odds,
status: bet.status,
event: bet.event
};
}Step 6: Account Management Widget
6.1 Implement the Pre-Built Management Widget
SharpSports provides a pre-built account management widget that handles all account operations (refresh, pause, remove, reverify) with a consistent UI:
<!-- Add the account management widget to your page -->
<div id="SSAccountManager"></div>
<script src="https://d388bvybj12fcd.cloudfront.net/account-manager.js"
token="YOUR_PUBLIC_API_KEY"
internalId="user_123"
theme="light"
showRefreshButton="true"
showPauseButton="true"
showRemoveButton="true">
</script>6.2 Widget Configuration Options
| Parameter | Type | Description |
|---|---|---|
token | string | Your public API key (required) |
internalId | string | Your user's unique ID (required) |
theme | string | Widget theme: 'light' or 'dark' |
showRefreshButton | boolean | Show manual refresh option |
showPauseButton | boolean | Allow pausing accounts |
showRemoveButton | boolean | Allow removing accounts |
onAccountStatusChange | function | Callback for status changes |
6.3 Handle Widget Events
// Listen for account status changes from the widget
window.addEventListener('SSAccountStatusChange', (event) => {
const { accountId, status, action } = event.detail;
console.log(`Account ${accountId} status: ${status}`);
switch(action) {
case 'refresh_started':
showLoadingIndicator();
break;
case 'account_paused':
updateAccountUI(accountId, 'paused');
break;
case 'account_removed':
removeAccountFromUI(accountId);
break;
case 'reverification_needed':
promptUserToRelink(accountId);
break;
}
});6.4 Account States in the Widget
The widget automatically handles these account states:
| State | Widget Display | User Actions Available |
|---|---|---|
| Verified | ✅ Connected | Refresh, Pause, Remove |
| Unverified | ⚠️ Needs Attention | Reverify, Remove |
| Unverifiable | ❌ Reconnect Required | Re-link, Remove |
| Paused | ⏸️ Paused | Resume, Remove |
| Refreshing | 🔄 Updating... | None (wait) |
Step 7: Testing with Sandbox
7.1 Available Test Users
| Username | Password | Behavior |
|---|---|---|
gooduser | Test1 | Successful verification, immediate response |
realuser | Test2 | Successful verification with realistic delays |
2FAuser | Test6 | Requires 2FA (use code: 123456) |
changepassworduser | Test5 | Simulates password change error |
errorloginuser | Test3 | Fails verification |
7.2 Test Flow Example
// 1. Create context with test internal ID
const context = await createContext('test_user_001');
// 2. Direct test user to link UI
const linkUrl = `https://ui.sharpsports.io/link/${context.cid}`;
console.log('Use test credentials:', {
username: 'gooduser',
password: 'Test1'
});
// 3. Your webhook will receive:
// - bettor.created
// - bettorAccount.verified
// - refreshResponse.created (with test bets)
// 4. Test refresh
const refreshResult = await refreshAllAccounts(bettorId);
// 5. Fetch and display test bets
const betSlips = await getBetSlips(bettorId);Step 8: Go Live
8.1 Switch to Production
- Sign up for a paid plan in the SharpSports Dashboard
- Toggle from Sandbox to Live mode
- Get your production API keys
- Update your API key in your application
- Test with a real sportsbook account
8.2 Production Checklist
- Webhook endpoint handles all event types
- Error handling for failed refreshes
- UI shows account status clearly
- Account management widget implemented
- Manual refresh controls in place
- Rate limiting implemented (1-2 refreshes per day max)
- User notifications for account issues
- Secure storage of bettor IDs and internal IDs
- HTTPS for all API calls
- SDK properly integrated and configured
Common Integration Patterns
Pattern 1: Login-Time Refresh
// When user logs into your app
async function onUserLogin(userId) {
const bettorId = await getBettorId(userId);
if (bettorId) {
// Trigger background refresh
refreshAllAccounts(bettorId);
// Show cached bets immediately
const cachedBets = await getCachedBets(userId);
displayBets(cachedBets);
}
}Pattern 2: Pull-to-Refresh
// Mobile app pull-to-refresh
async function onPullToRefresh(bettorId) {
setRefreshing(true);
try {
await refreshAllAccounts(bettorId);
// Wait for webhook or poll for updates
await waitForRefreshComplete(bettorId, { timeout: 30000 });
const betSlips = await getBetSlips(bettorId);
updateUI(betSlips);
} finally {
setRefreshing(false);
}
}Pattern 3: Account Status Banner
// Show banner when account needs attention
function AccountStatusBanner({ account }) {
if (!account.verified && !account.isUnverifiable) {
return (
<Banner type="warning">
Account needs reverification
<Button onClick={() => reverifyAccount(account.id)}>
Reverify Now
</Button>
</Banner>
);
}
if (account.isUnverifiable) {
return (
<Banner type="error">
Please re-link your account
<Button onClick={() => openLinkFlow()}>
Re-link Account
</Button>
</Banner>
);
}
return null;
}Troubleshooting
Issue: Webhooks not received
- Verify webhook URL is publicly accessible
- Check URL is HTTPS (required for production)
- Ensure response is 200 within 10 seconds
- Check Dashboard webhook logs
Issue: Account becomes unverified
Common causes and solutions:
- Password changed: User must re-link account
- 2FA enabled: Implement 2FA handling flow
- Terms acceptance required: User must log into sportsbook directly
- Site maintenance: Wait and retry later
Issue: Incomplete bets
- This is normal for new/exotic bet types
- Display
bookDescriptionfield which always contains bet details - SharpSports continuously improves parsing coverage
Issue: Refresh takes too long
- SDK-required books may take 30-60 seconds
- Implement async handling with loading states
- Show progress indicators during refresh
- Process webhook responses asynchronously
Best Practices
-
Security
- Never expose private API keys
- Always use the required
internalIdto link users - Validate webhook signatures
- Use HTTPS for all communications
-
SDK Requirements
- Always use an official SharpSports SDK for account linking
- Keep SDKs updated to the latest version
- Follow platform-specific SDK documentation
-
Performance
- Cache bet data locally
- Implement pagination for large bet histories
- Use webhook events instead of polling
- Trigger refreshes strategically (login time, user-initiated)
-
User Experience
- Use the account management widget for consistent UI
- Provide clear manual refresh buttons
- Handle errors gracefully with user-friendly messages
- Notify users of account issues promptly
-
Rate Limiting
- Maximum 1-2 manual refreshes per account per day
- Implement backoff for failed requests
- Batch operations when possible
- Track refresh frequency per user
Next Steps
-
Explore Advanced Features:
- Bet statistics and analytics
- Multi-user betting groups
- Closing line value tracking
- Responsible gaming tools
-
Review API Reference:
- Complete endpoint documentation
- Object schemas and examples
- SDK method references
-
Join the Developer Community:
- Get help from other developers
- Share integration patterns
- Request new features
Support
- Documentation: docs.sharpsports.io
- Dashboard: app.sharpsports.io
- Email: [email protected]
- Discord: discord.gg/sharpsports
Last updated: January 2025
Updated 4 days ago