Managing social accounts
This recipe explains how to use Snag social integration API to let your users connect their social accounts to their Snag profile.
Overview
This guide explains how to use Snag's social integration API to let your users connect their social accounts to their Snag profile. After creating a user in Snag (as explained in Managing User Accounts), you can use the social authentication endpoints to connect various social platforms to that user's profile.
You must first create a user using the Managing User
Accounts guide to obtain a userId before
connecting social accounts. The userId parameter required for all social
account connections comes from the user creation endpoint response.
Supported Social Platforms
Snag supports connecting the following social platforms:
- Discord
- Telegram
- Epic Games
- Steam
- Tiktok
- Phone
Connecting a Social Account
Endpoint
GET /api/{authType}/auth
Where {authType} is one of: twitter, discord, telegram, epic, steam , google , Reddit or tiktok.
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| userId | UUID | Yes | The ID of the user received from the user creation endpoint. This must be obtained by creating a user first. |
| websiteId | UUID | No | The ID of the website (if applicable) |
| redirect | String | Yes | The URL where the user will be redirected after completing authentication |
| responseType | String | No | The type of response to return (redirect or json). Defaults to redirect |
Security Note regarding Redirect URIs
To simplify integration, we allow unvalidated redirect URIs when using the API. This means you can set the redirect URL to any destination.
Since this endpoint is protected by your API key, only authorized requests can initiate the flow with a custom redirect. However, because the redirect URL can be set to anything, it is your responsibility to ensure that you do not include sensitive data in the URL parameters.
Example Request
GET /api/twitter/auth?userId=123e4567-e89b-12d3-a456-426614174001&redirect=https://your-app.com/auth-callback
Authentication Flow
- Call the
/api/{authType}/authendpoint with the required parameters - The API will return a URL that you should redirect your user to
- The user will authenticate with the social platform and grant permissions
- After successful authentication, the user will be redirected to the URL specified in the
redirectparameter - The social account is now connected to the user's Snag profile
Connecting Email
Use this flow to associate and verify a user's email with their Snag profile.
Endpoint
GET /api/email/auth
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| emailAddress | String | Yes | The user's email address to verify. |
| redirect | String | No | URL to redirect the user to after verification. We append status and, on failure, optional error. |
| userId | UUID | Conditional | Required when calling server-to-server with an API key to target a specific user. |
| walletAddress | String | Conditional | Alternative to userId when calling server-to-server with an API key; creates/links a user by wallet. |
| websiteId | UUID | No | Your website identifier if applicable. |
Headers
x-api-key: Required.
Flow
Call the endpoint with emailAddress (and optional redirect). This stores the pending email on the user's metadata and sends a verification email containing a secure link.
curl -X GET 'https://admin.snagsolutions.io/api/email/auth' \
-H 'x-api-key: YOUR_SNAG_API_KEY' \
-G \
--data-urlencode 'emailAddress=user@example.com' \
--data-urlencode 'userId=123e4567-e89b-12d3-a456-426614174001' \
--data-urlencode 'redirect=https://your-app.com/email-verified'{ "message": "Verification email sent.", "userId": "123e4567-e89b-12d3-a456-426614174001" }The email contains a link to GET /api/email/auth/connect?token=.... Snag
validates the token and verifies the email.
After verification, the user is redirected to your redirect URL (or a default verification page) with a status:
status=SUCCESSon successstatus=EXPIREDif the link expiredstatus=INVALIDanderror=INVALID_CODEfor invalid tokens
Example: https://your-app.com/email-verified?status=SUCCESS
Handling Email Account Conflicts
If the email is already verified for another user on the same website, the user is redirected to your redirect with error=MAXIMUM_ACCOUNT_LINKED. Handle this scenario the same way as described in Handling Social Account Conflicts.
Connecting Phone
Use this flow to associate and verify a user's phone number with their Snag profile. Phone verification uses Twilio Verify to send an OTP code via SMS.
Phone numbers must be in E.164
format (e.g.,
+14155552671). This is the international phone number format required by
Twilio.
Step 1: Send Verification Code
Endpoint
POST /api/phone/auth
Headers
x-api-key: Required.Content-Type:application/json
Body Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| phoneNumber | String | Yes | The phone number to verify in E.164 format (e.g., +14155552671). |
| userId | UUID | Conditional | Required when calling server-to-server with an API key to target a specific user. |
| walletAddress | String | Conditional | Alternative to userId when calling server-to-server with an API key. |
| websiteId | UUID | No | Your website identifier if applicable. |
Step 2: Verify OTP Code
Endpoint
POST /api/phone/auth/connect
Headers
x-api-key: Required.Content-Type:application/json
Body Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| phoneNumber | String | Yes | The phone number being verified, in E.164 format. |
| code | String | Yes | The OTP code received via SMS. |
| userId | UUID | Conditional | Required when calling server-to-server with an API key to target a specific user. |
| walletAddress | String | Conditional | Alternative to userId when calling server-to-server with an API key. |
| websiteId | UUID | No | Your website identifier if applicable. |
Flow
Call POST /api/phone/auth with the phone number. Snag sends an OTP code to the user via SMS using Twilio Verify.
curl -X POST 'https://admin.snagsolutions.io/api/phone/auth' \
-H 'x-api-key: YOUR_SNAG_API_KEY' \
-H 'Content-Type: application/json' \
-d '{
"phoneNumber": "+14155552671",
"userId": "123e4567-e89b-12d3-a456-426614174001"
}'{
"message": "Verification code sent.",
"userId": "123e4567-e89b-12d3-a456-426614174001"
}The user receives a 6-digit OTP code via SMS on their phone.
Call POST /api/phone/auth/connect with the phone number and the OTP code. Snag verifies the code with Twilio and connects the phone number to the user's profile.
curl -X POST 'https://admin.snagsolutions.io/api/phone/auth/connect' \
-H 'x-api-key: YOUR_SNAG_API_KEY' \
-H 'Content-Type: application/json' \
-d '{
"phoneNumber": "+14155552671",
"code": "123456",
"userId": "123e4567-e89b-12d3-a456-426614174001"
}'{
"success": true,
"message": "Phone number verified."
}Disconnecting Phone
To disconnect a phone number from a user's profile:
POST /api/phone/auth/disconnect
curl -X POST 'https://admin.snagsolutions.io/api/phone/auth/disconnect' \
-H 'x-api-key: YOUR_SNAG_API_KEY' \
-H 'Content-Type: application/json' \
-d '{
"userId": "123e4567-e89b-12d3-a456-426614174001"
}'{
"message": "Phone disconnected."
}Handling Phone Account Conflicts
If the phone number is already verified for another user on the same website, the verify endpoint returns error: 'MAXIMUM_ACCOUNT_LINKED' in the response body:
{
"success": false,
"error": "MAXIMUM_ACCOUNT_LINKED",
"message": "This phone number is already linked to another account."
}
Handle this scenario the same way as described in Handling Social Account Conflicts.
Handling Social Account Conflicts
If the social account is already associated with a different user, the redirect URL will include two query parameters:
error = 'MAXIMUM_ACCOUNT_LINKED'- Indicates that the account is already linked to another useraccountLinkData- A JWT verification token that contains the necessary information to process the account transfer
In this case, you should:
- Display a confirmation prompt to the user asking if they want to disconnect the account from the other user and link it to their current profile
- If the user confirms, make a POST request to
/api/users/verifywith the following payload:
{
"accountLinkData": "jwt_verification_token",
"userId": "current_user_id"
}
This will:
- Disconnect the social account from the previous user
- Connect it to the current user's profile
Google OAuth YouTube Channel Errors
When using Google OAuth, if the user's YouTube channel does not exist, the API will return an error with error = 'YOUTUBE_CHANNEL_NOT_FOUND'. This typically happens when:
- The user has never created a YouTube channel
- The YouTube channel was deleted or suspended
- The user's Google account doesn't have YouTube access
To handle this error, you should:
- Display a user-friendly message explaining that a YouTube channel is required
- Prompt the user to create a YouTube channel first
- Provide instructions on how to create a YouTube channel
- Allow the user to retry the Google OAuth flow after creating their channel
YouTube channels are automatically created when users upload their first video or customize their channel. Users can also manually create a channel by visiting youtube.com and following the setup prompts.
Google OAuth Requirements
When using Google OAuth integration, whether with Snag's default OAuth application or your own custom OAuth application, you need to ensure the following permissions (scopes) are configured:
https://www.googleapis.com/auth/youtube.readonly- Required to read YouTube channel informationhttps://www.googleapis.com/auth/userinfo.email- Required to access user email addresshttps://www.googleapis.com/auth/userinfo.profile- Required to access user profile information
Google requires verification of OAuth applications that request sensitive scopes. If you're using your own custom OAuth application, you must complete the verification process in the Google Cloud Console before the OAuth flow will work properly. This verification process can take several days to complete.
Contact the Snag team if you need assistance with the Google OAuth verification process or if you're experiencing issues with Google authentication.
Example Implementation with Conflict Handling
// Server-side implementation (e.g., in Node.js/Express)
app.get('/auth-callback', async (req, res) => {
const { error, accountLinkData } = req.query
const userId = req.session.userId // Get from authenticated session
if (error === 'MAXIMUM_ACCOUNT_LINKED') {
// Render a confirmation page or return JSON response
return res.json({
requiresConfirmation: true,
accountLinkData,
})
}
if (error === 'YOUTUBE_CHANNEL_NOT_FOUND') {
// Handle YouTube channel not found error
return res.json({
error: 'YOUTUBE_CHANNEL_NOT_FOUND',
message:
'YouTube channel not found. Please create a YouTube channel first and then re-authenticate via Google OAuth.',
requiresChannelCreation: true,
})
}
// Handle successful connection
res.redirect('/profile')
})
// Handle confirmation
app.post('/confirm-account-transfer', async (req, res) => {
const { accountLinkData, userId } = req.body
try {
const response = await fetch(
'https://admin.snagsolutions.io/api/users/verify',
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-api-key': `${process.env.SNAG_API_KEY}`, // API key stored securely on server
},
body: JSON.stringify({
accountLinkData,
userId,
}),
}
)
if (response.ok) {
res.redirect('/profile')
} else {
throw new Error('Failed to transfer account')
}
} catch (error) {
console.error('Error transferring account:', error)
res.status(500).send('Error transferring account')
}
})
Example Tiktok Implementation
// Server-side implementation (e.g., in Node.js/Express)
app.get('/connect-tiktok', async (req, res) => {
const userId = req.query.userId // Get from authenticated session
const redirectUrl = 'https://example.com/tiktok-redirect'
try {
// Step 1: Get state (JWT) from your server
// This initiates the TikTok OAuth flow and returns a JWT containing the verification code
const response = await fetch(
`https://admin.snagsolutions.io/api/tiktok/auth?userId=${userId}&redirect${redirect_uri}`,
{
headers: {
'x-api-key': `${process.env.SNAG_API_KEY}`, // API key stored securely on server
},
}
)
const data = await response.json()
// The response contains a `state` (JWT).
// Decode the JWT to extract the 8-digit code that the user must add to their TikTok bio.
res.json(data)
} catch (error) {
console.error('Error initiating TikTok connection:', error)
res.status(500).send('Error initiating TikTok connection')
}
})
app.get('/tiktok-redirect', async (req, res) => {
const { error, state, code } = req.query
if (error === 'INVALID_TIKTOK_CODE') {
// Step 1: Handle TikTok verification failure
// - show error to user
// - Log the failure for debugging
// - Optionally redirect to error page or show error message
}
// Step 2: If no error,
// - Call user endpoint
// - Verify the TikTok connection was successful (tiktokUser,tiktokUserId,tiktokVerifiedAt)
})
// Client-side code to initiate the flow
function decodeJwtForCode(stateJwt) {
if (!stateJwt || typeof stateJwt !== 'string') {
throw new Error('Invalid state JWT')
}
const decoded = jwt.decode(stateJwt)
if (decoded && typeof decoded === 'object' && 'generatedCode' in decoded) {
return decoded.generatedCode
}
throw new Error('generatedCode not found in JWT')
}
async function connectTiktokAccount(userId, tiktokProfileUrl) {
// Step 1: Get state (JWT) from your server
const response = await fetch(`/connect-tiktok?userId=${userId}`)
const { state } = await response.json()
// Decode JWT → extract the 8-digit code
const code = decodeJwtForCode(state)
// Show popup asking user to add code to TikTok bio
if (
window.confirm(
`Please add this code to your TikTok bio: ${code}\n\nClick OK once you have updated your bio.`
)
) {
// Step 2: Redirect user to TikTok connect with state and tiktokProfileUrl
// After verification, they'll be redirected back to your redirect URL
const connectUrl = `https://admin.snagsolutions.io/api/tiktok/auth/connect?state=${state}&tiktokProfileUrl=${encodeURIComponent(
tiktokProfileUrl
)}`
window.location.href = connectUrl
} else {
console.log('User cancelled TikTok bio update')
}
}
Example Reddit Implementation
Reddit integration only supports JSON response type. The redirect parameter
and responseType=redirect are not supported for Reddit authentication.
// Server-side implementation (e.g., in Node.js/Express)
app.get('/connect-reddit', async (req, res) => {
const userId = req.query.userId // Get from authenticated session
try {
// Step 1: Get state (JWT) from your server
// This initiates the Reddit verification flow and returns a JWT containing the verification code
const response = await fetch(
`https://admin.snagsolutions.io/api/reddit/auth?userId=${userId}&responseType=json`,
{
headers: {
'x-api-key': `${process.env.SNAG_API_KEY}`, // API key stored securely on server
},
}
)
const data = await response.json()
// The response contains a `state` (JWT).
// Decode the JWT to extract the 8-digit code that the user must add to their Reddit bio.
res.json(data)
} catch (error) {
console.error('Error initiating Reddit connection:', error)
res.status(500).send('Error initiating Reddit connection')
}
})
// Client-side code to initiate the flow
function decodeJwtForCode(stateJwt) {
if (!stateJwt || typeof stateJwt !== 'string') {
throw new Error('Invalid state JWT')
}
const decoded = jwt.decode(stateJwt)
if (decoded && typeof decoded === 'object' && 'generatedCode' in decoded) {
return decoded.generatedCode
}
throw new Error('generatedCode not found in JWT')
}
async function connectRedditAccount(userId, redditProfileUrl) {
// Step 1: Get state (JWT) from your server
const response = await fetch(`/connect-reddit?userId=${userId}`)
const { state } = await response.json()
// Decode JWT → extract the 8-digit code
const code = decodeJwtForCode(state)
// Show popup asking user to add code to Reddit bio
if (
window.confirm(
`Please add this code to your Reddit bio: ${code}\n\nClick OK once you have updated your bio.`
)
) {
// Step 2: Call Reddit connect API with state and redditProfileUrl
const connectUrl = `https://admin.snagsolutions.io/api/reddit/auth/connect?state=${state}&redditProfileUrl=${encodeURIComponent(
redditProfileUrl
)}`
const connectResponse = await fetch(connectUrl)
const result = await connectResponse.json()
if (result.success) {
alert('Reddit account connected successfully!')
} else {
alert(result.message || 'Failed to connect Reddit account')
}
} else {
console.log('User cancelled Reddit bio update')
}
}
Example Implementation
// Server-side implementation (e.g., in Node.js/Express)
app.get('/connect-twitter', async (req, res) => {
const userId = req.query.userId // Get from authenticated session
try {
const response = await fetch(
`https://admin.snagsolutions.io/api/twitter/auth?userId=${userId}&redirect=https://your-app.com/auth-callback`,
{
headers: {
'x-api-key': `${process.env.SNAG_API_KEY}`, // API key stored securely on server
},
}
)
const data = await response.json()
// Redirect the user to the authentication URL
res.redirect(data.url)
} catch (error) {
console.error('Error connecting Twitter account:', error)
res.status(500).send('Error connecting Twitter account')
}
})
// Client-side code to initiate the flow
function connectTwitterAccount(userId) {
// Redirect to your server endpoint
window.location.href = `/connect-twitter?userId=${userId}`
}
Response Handling
Success Response (200 OK)
{
"url": "https://auth-provider.com/oauth/authorize?client_id=xxx&redirect_uri=xxx&state=xxx"
}
The url property contains the authentication URL that you should redirect your user to.
Custom OAuth Applications
Snag allows you to use your own OAuth applications for Twitter, Discord, Epic Games, Google, and Steam integrations. This enables you to maintain your brand identity throughout the authentication flow and have more control over the user experience.
To configure your own OAuth credentials, navigate to the Integrations tab in your admin dashboard and enter your client ID and client secret for the desired platform. The changes take effect immediately for all new authentication requests.
You will also need to add the following redirect URLs to your OAuth application:
https://snag-render.com/api/twitter/auth/callbackhttps://snag-render.com/api/discord/auth/callbackhttps://snag-render.com/api/epic/auth/callbackhttps://snag-render.com/api/steam/auth/callbackhttps://snag-render.com/api/google/auth/callback
Google OAuth Application Setup
When configuring a custom Google OAuth application, ensure your OAuth consent screen includes the following required scopes:
https://www.googleapis.com/auth/youtube.readonlyhttps://www.googleapis.com/auth/userinfo.emailhttps://www.googleapis.com/auth/userinfo.profile
Google requires verification of OAuth applications requesting sensitive scopes. You must complete the verification process in the Google Cloud Console before your custom OAuth application will work. The verification process typically takes several days and may require additional documentation about your application's use of these scopes.
Troubleshooting
403 Forbidden Response
If you receive a 403 Forbidden response when attempting to connect a social account, check the following:
Ensure you're including the x-api-key header in your request with a valid API key. The API key must be associated with your Snag account and have the necessary permissions.
headers: {
'x-api-key': 'YOUR_SNAG_API_KEY'
}Verify that the userId you're using:
- Was obtained from the user creation endpoint
- Uses the
userIdfield from the response, not theidfield - Is a valid UUID format
- Belongs to a user that exists in your Snag account
- Is associated with the same website/account as your API key
Make sure to use userId from the user creation response, not id. Using
id instead of userId will result in a 403 Forbidden error.
If you haven't created the user yet, follow the Managing User Accounts guide first.
Your API key may not have the required permissions to connect social accounts. Contact the Snag team to verify your API key has the necessary scopes enabled.
If you're using a websiteId parameter, ensure it matches the website associated with your API key and the user's account. Mismatched website IDs can result in 403 errors.
Next Steps
After connecting social accounts, you can use this information to enhance the user experience in your application and leverage it for loyalty program features.