Latest commit

History

13 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

SCS Python SDK

Python SDK for SCS (Spyxpo Cloud Services)

Installation

pip install scs-sdk

For realtime functionality (WebSocket support):

pip install scs-sdk[realtime]

Quick Start

fromscsimportSCS# Initialize from config filescs=SCS.initialize_app('./scs-info.json')
# Or initialize with config dictscs=SCS({
'api_key': 'your-api-key',
'project_id': 'your-project-id',
'base_url': 'https://your-scs-instance.com'
})
# Authenticationuser=scs.auth.login(email='user@example.com', password='password123')
# Databaseusers=scs.database.collection('users').where('age', '>=', 18).limit(10).get()
# Storagescs.storage.upload('./image.png', folder='images')

Services

Authentication

# Register a new useruser=scs.auth.register(
email='user@example.com',
password='password123',
display_name='John Doe'
)
# Loginuser=scs.auth.login(email='user@example.com', password='password123')
# Get current userme=scs.auth.get_current_user()
# Update profilescs.auth.update_profile(display_name='Jane Doe')
# Change passwordscs.auth.change_password(
current_password='old123',
new_password='new456'
)
# Logoutscs.auth.logout()

OAuth and Social Sign-In

SCS supports multiple OAuth providers for seamless social authentication. Each provider follows a similar pattern but requires different credentials obtained from their respective SDKs.

Google Sign-In

Authenticate users with their Google account. Requires the Google Sign-In SDK on the client.

# Sign in with Googleuser=scs.auth.sign_in_with_google(
id_token='google-id-token', # Required: ID token from Google Sign-Inaccess_token='google-access-token'# Optional: Access token for additional scopes
)
print(f"User ID: {user['uid']}")
print(f"Email: {user['email']}")
print(f"Display Name: {user['displayName']}")
print(f"Photo URL: {user['photoURL']}")
print(f"Provider: {user['providerId']}") # 'google'

Parameters:

ParameterTypeRequiredDescription
id_tokenstrYesThe ID token obtained from Google Sign-In SDK
access_tokenstrNoAccess token for additional Google API scopes

Returns:dict with user data and token

Example with Google OAuth Library:

fromgoogle.oauth2importid_tokenfromgoogle.auth.transportimportrequests# Verify and get token on server sidedefverify_google_token(token):
try:
idinfo=id_token.verify_oauth2_token(
token, requests.Request(), GOOGLE_CLIENT_ID
)
returnidinfoexceptValueError:
returnNone# Sign in with the tokenuser=scs.auth.sign_in_with_google(id_token=google_token)
Facebook Sign-In

Authenticate users with their Facebook account.

# Sign in with Facebookuser=scs.auth.sign_in_with_facebook(
access_token='facebook-access-token'# Required: Access token from Facebook Login
)
print(f"User: {user['displayName']}")
print(f"Email: {user['email']}") # May be None if user didn't grant email permission

Parameters:

ParameterTypeRequiredDescription
access_tokenstrYesAccess token from Facebook Login SDK

Example with Facebook SDK:

# After getting access token from Facebook SDK on clientdeffacebook_login(access_token):
try:
user=scs.auth.sign_in_with_facebook(access_token=access_token)
returnuserexceptExceptionase:
print(f"Facebook login failed: {e}")
returnNone
Apple Sign-In

Authenticate users with their Apple ID. Ideal for iOS apps and required for apps with social login on the App Store.

# Sign in with Appleuser=scs.auth.sign_in_with_apple(
identity_token='apple-identity-token', # Required: Identity token from Sign in with Appleauthorization_code='apple-auth-code', # Optional: Authorization code for server verificationfull_name='John Doe'# Optional: User's name (only available on first sign-in)
)

Parameters:

ParameterTypeRequiredDescription
identity_tokenstrYesJWT identity token from Sign in with Apple
authorization_codestrNoAuthorization code for additional verification
full_namestrNoUser's full name (Apple only provides this on first sign-in)

Important Notes:

  • Apple only provides the user's name on the first sign-in. Store it immediately.
  • Users can choose to hide their email (Apple provides a relay email).
  • Required for apps using social login on iOS/macOS.
GitHub Sign-In

Authenticate users with their GitHub account. Popular for developer-focused applications.

# Sign in with GitHubuser=scs.auth.sign_in_with_github(
code='github-oauth-code', # Required: OAuth authorization coderedirect_uri='https://yourapp.com/callback'# Optional: Must match OAuth app settings
)
print(f"GitHub username: {user['displayName']}")
print(f"Email: {user['email']}")

Parameters:

ParameterTypeRequiredDescription
codestrYesOAuth authorization code from GitHub OAuth flow
redirect_uristrNoRedirect URI (must match your GitHub OAuth App settings)

OAuth Flow Example (Flask):

fromflaskimportFlask, redirect, requestimportrequestsapp=Flask(__name__)
GITHUB_CLIENT_ID='your-client-id'GITHUB_CLIENT_SECRET='your-client-secret'@app.route('/login/github')defgithub_login():
returnredirect(
f'https://github.com/login/oauth/authorize'f'?client_id={GITHUB_CLIENT_ID}'f'&redirect_uri=https://yourapp.com/callback'f'&scope=read:user user:email'
)
@app.route('/callback')defgithub_callback():
code=request.args.get('code')
ifcode:
user=scs.auth.sign_in_with_github(
code=code,
redirect_uri='https://yourapp.com/callback'
)
returnf"Welcome, {user['displayName']}!"return"Login failed"
Twitter/X Sign-In

Authenticate users with their Twitter/X account using OAuth 1.0a.

# Sign in with Twitter/Xuser=scs.auth.sign_in_with_twitter(
oauth_token='twitter-oauth-token', # Required: OAuth tokenoauth_token_secret='twitter-oauth-secret'# Required: OAuth token secret
)

Parameters:

ParameterTypeRequiredDescription
oauth_tokenstrYesOAuth token from Twitter authentication
oauth_token_secretstrYesOAuth token secret from Twitter authentication

Note: Twitter uses OAuth 1.0a which requires a more complex flow. Consider using a library like tweepy or python-twitter.

Microsoft Sign-In

Authenticate users with their Microsoft account (personal, work, or school accounts).

# Sign in with Microsoftuser=scs.auth.sign_in_with_microsoft(
access_token='microsoft-access-token', # Required: Access token from MSALid_token='microsoft-id-token'# Optional: ID token for additional claims
)

Parameters:

ParameterTypeRequiredDescription
access_tokenstrYesAccess token from Microsoft Authentication Library (MSAL)
id_tokenstrNoID token for additional user claims

Example with MSAL Python:

frommsalimportConfidentialClientApplicationapp=ConfidentialClientApplication(
client_id="your-client-id",
authority="https://login.microsoftonline.com/common",
client_credential="your-client-secret"
)
# After getting tokens from authorization code flowresult=app.acquire_token_by_authorization_code(
code,
scopes=["User.Read"],
redirect_uri="https://yourapp.com/callback"
)
if"access_token"inresult:
user=scs.auth.sign_in_with_microsoft(
access_token=result["access_token"],
id_token=result.get("id_token")
)

Anonymous Authentication

Allow users to use your app without creating an account. Anonymous accounts can later be upgraded to permanent accounts by linking a provider.

# Sign in anonymously (creates a temporary account)user=scs.auth.sign_in_anonymously(
custom_data={'referrer': 'landing-page', 'campaign': 'summer-sale'} # optional
)
print(f"Anonymous user ID: {user['uid']}")
print(f"Is anonymous: {user['isAnonymous']}") # True

Parameters:

ParameterTypeRequiredDescription
custom_datadictNoCustom data to store with the anonymous user for analytics

Use Cases:

  • Allow users to try your app before signing up
  • Guest checkout in e-commerce
  • Save user progress/preferences before account creation
  • A/B testing with user tracking

Converting Anonymous to Permanent Account:

# User decides to create a permanent account# Link their anonymous account to a providertry:
user=scs.auth.link_provider('google', {
'idToken': 'google-id-token'
})
print("Account upgraded! User data preserved.")
print(f"Is anonymous: {user['isAnonymous']}") # FalseexceptExceptionase:
if'credential-already-in-use'instr(e):
# This Google account is already linked to another userprint("This account is already registered. Please sign in instead.")
else:
raise

Phone Number Authentication

Two-step authentication flow using SMS verification codes.

# Step 1: Send verification code to phoneresult=scs.auth.send_phone_verification_code(
phone_number='+1234567890', # Required: E.164 formatrecaptcha_token='recaptcha-token'# Optional: For bot protection
)
verification_id=result['verificationId']
print(f"Verification ID: {verification_id}")
# Store this ID - you'll need it in step 2# Step 2: User enters the code they receiveduser=scs.auth.sign_in_with_phone_number(
verification_id=verification_id, # The ID from step 1code='123456'# 6-digit code from SMS
)
print(f"Phone verified: {user['phoneNumber']}")

send_phone_verification_code Parameters:

ParameterTypeRequiredDescription
phone_numberstrYesPhone number in E.164 format (e.g., +1234567890)
recaptcha_tokenstrNoreCAPTCHA token for abuse prevention

sign_in_with_phone_number Parameters:

ParameterTypeRequiredDescription
verification_idstrYesVerification ID from send_phone_verification_code
codestrYes6-digit verification code from SMS

Complete Flow Example:

defsign_in_with_phone(phone_number: str, get_code_callback) ->dict:
""" Sign in with phone number. Args: phone_number: Phone number in E.164 format get_code_callback: Function that prompts user for the SMS code Returns: User dict on success """try:
# Step 1: Send coderesult=scs.auth.send_phone_verification_code(
phone_number=phone_number
)
verification_id=result['verificationId']
# Get code from user (your UI implementation)code=get_code_callback()
# Step 2: Verify codeuser=scs.auth.sign_in_with_phone_number(
verification_id=verification_id,
code=code
)
returnuserexceptExceptionase:
error_msg=str(e).lower()
if'invalid-phone-number'inerror_msg:
raiseValueError('Invalid phone number format. Use E.164 format (+1234567890)')
elif'too-many-requests'inerror_msg:
raiseValueError('Too many attempts. Please try again later.')
elif'invalid-verification-code'inerror_msg:
raiseValueError('Invalid verification code. Please try again.')
elif'code-expired'inerror_msg:
raiseValueError('Code expired. Please request a new one.')
else:
raise

Custom Token Authentication

Sign in using a JWT token generated by your own backend. Useful for migrating users from another system or integrating with custom authentication.

# Sign in with a custom token (generated by your backend)user=scs.auth.sign_in_with_custom_token('your-custom-jwt-token')
print(f"Signed in user: {user['uid']}")

Parameters:

ParameterTypeRequiredDescription
tokenstrYesJWT token generated by your backend

Backend Token Generation Example:

importjwtimporttimeimportosdefcreate_custom_token(uid: str, claims: dict=None) ->str:
""" Create a custom authentication token. Args: uid: Unique user identifier claims: Optional custom claims to include Returns: JWT token string """payload= {
'uid': uid,
'claims': claimsor {},
'iat': int(time.time()),
'exp': int(time.time()) +3600# 1 hour
}
returnjwt.encode(
payload,
os.environ['SCS_SECRET_KEY'],
algorithm='HS256'
)
# Generate token for a usercustom_token=create_custom_token('user-123', {'role': 'admin'})
# Send this token to the client for sign-in

Use Cases:

  • Migrating users from another authentication system
  • Server-side user creation with immediate client sign-in
  • Integration with enterprise SSO systems (SAML, LDAP)
  • Machine-to-machine authentication

Account Linking

Link multiple authentication providers to a single account. Users can sign in with any linked provider.

# Link a provider to current accountuser=scs.auth.link_provider('facebook', {
'accessToken': 'facebook-access-token'
})
print(f"Linked providers: {user['providerData']}")
# [{'providerId': 'password'}, {'providerId': 'google'}, {'providerId': 'facebook'}]# Unlink a provider from current accountuser=scs.auth.unlink_provider('facebook')
print(f"Remaining providers: {user['providerData']}")
# Get available sign-in methods for an emailresult=scs.auth.fetch_sign_in_methods_for_email('user@example.com')
print(f"Available methods: {result['methods']}")
# ['password', 'google', 'facebook']

link_provider Parameters:

ParameterTypeRequiredDescription
providerstrYesProvider name: 'google', 'facebook', 'apple', 'github', 'twitter', 'microsoft'
credentialsdictYesProvider-specific credentials (tokens)

Supported Providers and Credentials:

ProviderRequired Credentials
google{'idToken': '...', 'accessToken': '...'} (accessToken optional)
facebook{'accessToken': '...'}
apple{'identityToken': '...', 'authorizationCode': '...', 'fullName': '...'}
github{'code': '...', 'redirectUri': '...'}
twitter{'oauthToken': '...', 'oauthTokenSecret': '...'}
microsoft{'accessToken': '...', 'idToken': '...'}

Complete Account Linking Flow:

defcan_link_provider(email: str, provider: str) ->bool:
"""Check if a provider can be linked to an account."""result=scs.auth.fetch_sign_in_methods_for_email(email)
methods=result.get('methods', [])
ifproviderinmethods:
raiseValueError(f"{provider} is already linked to this account")
returnTruedeflink_google_account(google_id_token: str) ->dict:
"""Link Google to the current user's account."""try:
# Verify user is signed incurrent_user=scs.auth.get_current_user()
ifnotcurrent_user:
raiseValueError('Must be signed in to link accounts')
# Link the provideruser=scs.auth.link_provider('google', {
'idToken': google_id_token
})
print('Successfully linked Google account')
returnuserexceptExceptionase:
error_msg=str(e).lower()
if'credential-already-in-use'inerror_msg:
print('This Google account is already linked to another user')
elif'provider-already-linked'inerror_msg:
print('Google is already linked to this account')
raise

Password Reset & Email Verification

Handle password recovery and email verification flows.

# Send password reset emailscs.auth.send_password_reset_email('user@example.com')
print("Password reset email sent")
# Confirm password reset (user clicks link in email, you extract the code)scs.auth.confirm_password_reset(
code='reset-code-from-email', # Code from the reset linknew_password='newSecurePassword123'# User's new password
)
print("Password successfully reset")
# Send email verification to current userscs.auth.send_email_verification()
print("Verification email sent")
# Verify email with code (user clicks link, you extract the code)scs.auth.verify_email('verification-code')
print("Email verified")

Error Handling:

defreset_password(email: str) ->dict:
"""Send password reset email with error handling."""try:
scs.auth.send_password_reset_email(email)
return {'success': True, 'message': 'Reset email sent'}
exceptExceptionase:
error_msg=str(e).lower()
if'user-not-found'inerror_msg:
# Don't reveal if user exists for securityreturn {'success': True, 'message': 'If this email exists, a reset link was sent'}
elif'too-many-requests'inerror_msg:
return {'success': False, 'message': 'Too many attempts. Please try later.'}
else:
raisedefconfirm_reset(code: str, new_password: str) ->dict:
"""Confirm password reset with validation."""# Validate password strengthiflen(new_password) <8:
raiseValueError('Password must be at least 8 characters')
try:
scs.auth.confirm_password_reset(code=code, new_password=new_password)
return {'success': True}
exceptExceptionase:
error_msg=str(e).lower()
if'expired-action-code'inerror_msg:
return {'success': False, 'message': 'Reset link expired. Please request a new one.'}
elif'invalid-action-code'inerror_msg:
return {'success': False, 'message': 'Invalid reset link.'}
elif'weak-password'inerror_msg:
return {'success': False, 'message': 'Password is too weak.'}
else:
raise

Authentication State Management

Manage user sessions and authentication state.

# Check if user is logged inis_logged_in=scs.auth.is_logged_inprint(f"Is logged in: {is_logged_in}")
# Get current user (from cache)cached_user=scs.auth.current_user# Get current user (fresh from server)user=scs.auth.get_current_user()
# Refresh user datarefreshed_user=scs.auth.reload()
# Get the current auth tokentoken=scs.auth.token

Complete Authentication Example

fromscsimportSCS, AuthenticationErrorclassAuthService:
def__init__(self):
self.scs=SCS({
'api_key': 'your-api-key',
'project_id': 'your-project-id',
'base_url': 'https://your-scs-instance.com'
})
defregister(self, email: str, password: str, display_name: str) ->dict:
"""Register a new user with email verification."""user=self.scs.auth.register(
email=email,
password=password,
display_name=display_name
)
# Send verification emailself.scs.auth.send_email_verification()
returnuserdeflogin(self, email: str, password: str) ->dict:
"""Login with email and password."""returnself.scs.auth.login(email=email, password=password)
defsocial_login(self, provider: str, credentials: dict) ->dict:
"""Login with any social provider."""methods= {
'google': lambda: self.scs.auth.sign_in_with_google(**credentials),
'facebook': lambda: self.scs.auth.sign_in_with_facebook(**credentials),
'apple': lambda: self.scs.auth.sign_in_with_apple(**credentials),
'github': lambda: self.scs.auth.sign_in_with_github(**credentials),
'twitter': lambda: self.scs.auth.sign_in_with_twitter(**credentials),
'microsoft': lambda: self.scs.auth.sign_in_with_microsoft(**credentials)
}
ifprovidernotinmethods:
raiseValueError(f"Unknown provider: {provider}")
returnmethods[provider]()
defcontinue_as_guest(self, custom_data: dict=None) ->dict:
"""Sign in anonymously for guest access."""returnself.scs.auth.sign_in_anonymously(custom_data=custom_dataor {})
defupgrade_guest_account(self, provider: str, credentials: dict) ->dict:
"""Upgrade anonymous account to permanent."""user=self.scs.auth.get_current_user()
ifnotuserornotuser.get('isAnonymous'):
raiseValueError('Current user is not anonymous')
returnself.scs.auth.link_provider(provider, credentials)
deflogout(self):
"""Sign out the current user."""self.scs.auth.logout()
# Usageauth=AuthService()
# Register new useruser=auth.register('user@example.com', 'password123', 'John Doe')
# Or sign in with Googlegoogle_user=auth.social_login('google', {'id_token': 'xxx'})
# Or continue as guest and upgrade laterguest=auth.continue_as_guest({'source': 'homepage'})
# ... user decides to create account ...upgraded=auth.upgrade_guest_account('google', {'idToken': 'xxx'})

Database

Document database with query builder. SCS supports two powerful database options:

Database Types

TypeNameDescriptionBest For
eazieaZI DatabaseDocument-based NoSQL with Firestore-like collections, documents, and subcollectionsDevelopment, prototyping, small to medium apps
reladbRelaDBProduction-grade NoSQL database with relational-style viewsProduction, scalability, advanced queries

Initialize with eaZI (Default)

fromscsimportSCS# eaZI is the default database - no special configuration neededscs=SCS({
'project_id': 'your-project-id',
'api_key': 'your-api-key'# database_type: 'eazi' is implicit
})

Initialize with RelaDB (Production)

fromscsimportSCS# Use RelaDB for productionscs=SCS({
'project_id': 'your-project-id',
'api_key': 'your-api-key',
'database_type': 'reladb'# Enable RelaDB
})

eaZI Database Features

  • Document-based: Firestore-like collections and documents
  • Subcollections: Nested data organization
  • File-based storage: No external dependencies required
  • Zero configuration: Works out of the box
  • Query support: Filtering, ordering, and pagination

RelaDB Features

  • Production-ready: Built for reliability and performance
  • Scalable: Horizontal scaling and replication support
  • Advanced queries: Aggregation pipelines, complex filters
  • Indexing: Custom indexes for optimized performance
  • Schema flexibility: Dynamic schema with validation support
  • Relational-style views: Table view with columns and rows in the console

Collection Operations

# Get a collection referenceusers=scs.database.collection('users')
# List all collectionscollections=scs.database.list_collections()
# Create a collectionscs.database.create_collection('new_collection')
# Delete a collectionscs.database.delete_collection('old_collection')

Document Operations

# Add document with auto-generated IDdoc=scs.database.collection('users').add({
'name': 'John Doe',
'email': 'john@example.com',
'age': 30,
'tags': ['developer', 'python'],
'profile': {
'bio': 'Software developer',
'avatar': 'https://example.com/avatar.jpg'
}
})
print(f'Document ID: {doc["id"]}')
# Set document with custom ID (creates or overwrites)scs.database.collection('users').doc('user-123').set({
'name': 'Jane Doe',
'email': 'jane@example.com'
})
# Get a single documentuser=scs.database.collection('users').doc('user-123').get()
print(user)
# Update document (partial update)scs.database.collection('users').doc('user-123').update({
'age': 31,
'profile.bio': 'Senior developer'
})
# Delete documentscs.database.collection('users').doc('user-123').delete()

Query Operations

# Simple query with single filteractive_users=scs.database.collection('users') \
.where('status', '==', 'active') \
.get()
# Multiple filtersresults=scs.database.collection('users') \
.where('age', '>=', 18) \
.where('status', '==', 'active') \
.get()
# Ordering and paginationposts=scs.database.collection('posts') \
.where('published', '==', True) \
.order_by('created_at', 'desc') \
.limit(10) \
.skip(20) \
.get()
# Using 'in' operatorfeatured=scs.database.collection('posts') \
.where('category', 'in', ['tech', 'science', 'news']) \
.get()
# Using 'contains' for array fieldstagged=scs.database.collection('posts') \
.where('tags', 'contains', 'python') \
.get()

Query Operators

OperatorDescriptionExample
==Equal to.where('status', '==', 'active')
!=Not equal to.where('status', '!=', 'deleted')
>Greater than.where('age', '>', 18)
>=Greater than or equal.where('age', '>=', 18)
<Less than.where('price', '<', 100)
<=Less than or equal.where('price', '<=', 50)
inValue in array.where('status', 'in', ['active', 'pending'])
containsArray contains value.where('tags', 'contains', 'featured')

Subcollections

# Access a subcollectionposts_ref=scs.database \
.collection('users') \
.doc('user_id') \
.collection('posts')
# Add to subcollectionpost=posts_ref.add({
'title': 'My First Post',
'content': 'Hello World!',
'created_at': datetime.now().isoformat()
})
# Query subcollectionuser_posts=posts_ref \
.order_by('created_at', 'desc') \
.limit(5) \
.get()
# Nested subcollections (e.g., users/user_id/posts/post_id/comments)comments_ref=scs.database \
.collection('users') \
.doc('user_id') \
.collection('posts') \
.doc('post_id') \
.collection('comments')

Storage

# Upload from file pathfile_info=scs.storage.upload('./image.png')
# Upload with folderfile_info=scs.storage.upload('./doc.pdf', folder='documents')
# Upload from bytesfile_info=scs.storage.upload_bytes(
data=image_bytes,
filename='image.png',
content_type='image/png'
)
# List filesfiles=scs.storage.list_files(folder='images')
# Get file referencefile_ref=scs.storage.ref('file-id')
# Download filecontent=file_ref.download()
# Download to filefile_ref.download_to_file('./local_file.png')
# Get metadatametadata=file_ref.get_metadata()
# Delete filefile_ref.delete()
# Folder operationsscs.storage.create_folder('my-folder')
scs.storage.delete_folder('my-folder')

Realtime Database

Real-time data synchronization via WebSocket:

# Connect to realtime servicescs.realtime.connect()
# Get referencechat_ref=scs.realtime.ref('chat/room1')
# Subscribe to updatesdefon_message(data, event):
print(f'Event: {event}, Data: {data}')
unsubscribe=chat_ref.on(on_message)
# Subscribe to single updatechat_ref.once(lambdadata, event: print(data))
# Unsubscribeunsubscribe()
# Disconnectscs.realtime.disconnect()

Cloud Messaging

Push notifications with topics and direct messaging:

# Register device tokenscs.messaging.register_token(
token='device-fcm-token',
platform='android'
)
# Topic operationsscs.messaging.create_topic('news', description='News updates')
scs.messaging.subscribe_to_topic('news', 'device-token')
scs.messaging.unsubscribe_from_topic('news', 'device-token')
# Send to topicscs.messaging.send_to_topic(
topic='news',
title='Breaking News',
body='Something happened!',
data={'articleId': '123'}
)
# Send to specific devicescs.messaging.send_to_token(
token='device-token',
title='Personal Alert',
body='You have a new message'
)
# Send to multiple devicesscs.messaging.send_to_tokens(
tokens=['token1', 'token2'],
title='Broadcast',
body='Hello everyone!'
)

Remote Config

Dynamic app configuration:

# Fetch configurationconfig=scs.remote_config.fetch()
# Get values with type safetytheme=scs.remote_config.get_string('app_theme', default='light')
max_items=scs.remote_config.get_int('max_items', default=10)
enabled=scs.remote_config.get_bool('feature_enabled', default=False)
settings=scs.remote_config.get_json('settings', default={})
# Admin: Manage parametersscs.remote_config.create_param(
key='app_theme',
value='dark',
description='Default app theme'
)
scs.remote_config.update_param('app_theme', 'light')
scs.remote_config.delete_param('old_param')
# Publish changesscs.remote_config.publish()
# Version managementversions=scs.remote_config.list_versions()
scs.remote_config.rollback('version-id')

Serverless Functions

Execute custom code on the backend:

# Invoke a functionresult=scs.functions.invoke('processOrder', {
'orderId': '12345',
'action': 'confirm'
})
# Admin: Create a functionscs.functions.create(
name='processOrder',
code=''' module.exports = async (data, context) => { const { orderId, action } = data; // Process order... return { success: true, orderId }; }; ''',
runtime='nodejs18',
timeout=30000,
memory=256
)
# Admin: List functionsfunctions=scs.functions.list()
# Admin: Update functionscs.functions.update('function-id', code='...')
# Admin: Test functionresult=scs.functions.test('function-id', {'test': 'data'})
# Admin: Get logslogs=scs.functions.get_logs('function-id')

AI

Chat, text completion, and image generation with local LLM models:

# Chat with AIresponse=scs.ai.chat(
message="What is the capital of France?",
system_prompt="You are a helpful geography assistant."
)
print(response['content'])
# Text completionresponse=scs.ai.complete(prompt="Once upon a time")
print(response['content'])
# Generate imageresponse=scs.ai.generate_image(prompt="A sunset over mountains")
print(response['imageUrl'])
# List available modelsmodels=scs.ai.list_models()
# Conversation managementconversation=scs.ai.create_conversation(title="Geography Chat")
scs.ai.get_conversation(conversation['conversationId'])
scs.ai.delete_conversation(conversation['conversationId'])

AI Agents

Create and manage AI agents with custom instructions and tools:

# Create an agentagent=scs.ai.create_agent(
name="Customer Support",
instructions="You are a helpful customer support assistant. Be polite and helpful.",
model="llama3.2",
temperature=0.7
)
print(f"Created agent: {agent['agentId']}")
# List agentsagents=scs.ai.list_agents()
# Run the agentresponse=scs.ai.run_agent(
agent_id=agent['agentId'],
input="How do I reset my password?"
)
print(f"Agent: {response['output']}")
print(f"Session: {response['sessionId']}")
# Continue the conversation in the same sessionresponse=scs.ai.run_agent(
agent_id=agent['agentId'],
input="Thanks! What about enabling 2FA?",
session_id=response['sessionId']
)
# List agent sessionssessions=scs.ai.list_agent_sessions(agent['agentId'])
# Get full session historysession=scs.ai.get_agent_session(agent['agentId'], response['sessionId'])
formsginsession['messages']:
print(f"{msg['role']}: {msg['content']}")
# Update agentscs.ai.update_agent(
agent_id=agent['agentId'],
instructions="Updated instructions here",
temperature=0.5
)
# Define a tool for agentstool=scs.ai.define_tool(
name="get_weather",
description="Get weather for a location",
parameters={
"type": "object",
"properties": {
"location": {"type": "string", "description": "City name"}
}
}
)
# List toolstools=scs.ai.list_tools()
# Delete agent and sessionsscs.ai.delete_agent_session(agent['agentId'], response['sessionId'])
scs.ai.delete_agent(agent['agentId'])

Configuration File

Create an scs-info.json file:

{
"sdk_config": {
"api_key": "pk_your_api_key",
"project_id": "your-project-id",
"base_url": "https://your-scs-instance.com"
}
}

Error Handling

fromscsimportSCS, SCSError, AuthenticationError, NotFoundError, ValidationErrortry:
user=scs.auth.login(email='user@example.com', password='wrong')
exceptAuthenticationErrorase:
print(f'Auth failed: {e.message}')
print(f'Status: {e.status}')
exceptNotFoundErrorase:
print(f'Not found: {e.message}')
exceptValidationErrorase:
print(f'Validation error: {e.message}')
exceptSCSErrorase:
print(f'SCS error: {e.message}')

Context Manager

withSCS({'api_key': '...', 'project_id': '...'}) asscs:
users=scs.database.collection('users').get()
# Connection automatically closed

Requirements

  • Python 3.8+
  • requests
  • python-socketio[client] (optional, for realtime functionality)

License

MIT License

About

The official Python SDK for Spyxpo Cloud Services

Resources

Code of conduct

Contributing

Security policy

Stars

2 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Latest commit

History

13 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

SCS Python SDK

Python SDK for SCS (Spyxpo Cloud Services)

Installation

pip install scs-sdk

For realtime functionality (WebSocket support):

pip install scs-sdk[realtime]

Quick Start

fromscsimportSCS# Initialize from config filescs=SCS.initialize_app('./scs-info.json')
# Or initialize with config dictscs=SCS({
'api_key': 'your-api-key',
'project_id': 'your-project-id',
'base_url': 'https://your-scs-instance.com'
})
# Authenticationuser=scs.auth.login(email='user@example.com', password='password123')
# Databaseusers=scs.database.collection('users').where('age', '>=', 18).limit(10).get()
# Storagescs.storage.upload('./image.png', folder='images')

Services

Authentication

# Register a new useruser=scs.auth.register(
email='user@example.com',
password='password123',
display_name='John Doe'
)
# Loginuser=scs.auth.login(email='user@example.com', password='password123')
# Get current userme=scs.auth.get_current_user()
# Update profilescs.auth.update_profile(display_name='Jane Doe')
# Change passwordscs.auth.change_password(
current_password='old123',
new_password='new456'
)
# Logoutscs.auth.logout()

OAuth and Social Sign-In

SCS supports multiple OAuth providers for seamless social authentication. Each provider follows a similar pattern but requires different credentials obtained from their respective SDKs.

Google Sign-In

Authenticate users with their Google account. Requires the Google Sign-In SDK on the client.

# Sign in with Googleuser=scs.auth.sign_in_with_google(
id_token='google-id-token', # Required: ID token from Google Sign-Inaccess_token='google-access-token'# Optional: Access token for additional scopes
)
print(f"User ID: {user['uid']}")
print(f"Email: {user['email']}")
print(f"Display Name: {user['displayName']}")
print(f"Photo URL: {user['photoURL']}")
print(f"Provider: {user['providerId']}") # 'google'

Parameters:

ParameterTypeRequiredDescription
id_tokenstrYesThe ID token obtained from Google Sign-In SDK
access_tokenstrNoAccess token for additional Google API scopes

Returns:dict with user data and token

Example with Google OAuth Library:

fromgoogle.oauth2importid_tokenfromgoogle.auth.transportimportrequests# Verify and get token on server sidedefverify_google_token(token):
try:
idinfo=id_token.verify_oauth2_token(
token, requests.Request(), GOOGLE_CLIENT_ID
)
returnidinfoexceptValueError:
returnNone# Sign in with the tokenuser=scs.auth.sign_in_with_google(id_token=google_token)
Facebook Sign-In

Authenticate users with their Facebook account.

# Sign in with Facebookuser=scs.auth.sign_in_with_facebook(
access_token='facebook-access-token'# Required: Access token from Facebook Login
)
print(f"User: {user['displayName']}")
print(f"Email: {user['email']}") # May be None if user didn't grant email permission

Parameters:

ParameterTypeRequiredDescription
access_tokenstrYesAccess token from Facebook Login SDK

Example with Facebook SDK:

# After getting access token from Facebook SDK on clientdeffacebook_login(access_token):
try:
user=scs.auth.sign_in_with_facebook(access_token=access_token)
returnuserexceptExceptionase:
print(f"Facebook login failed: {e}")
returnNone
Apple Sign-In

Authenticate users with their Apple ID. Ideal for iOS apps and required for apps with social login on the App Store.

# Sign in with Appleuser=scs.auth.sign_in_with_apple(
identity_token='apple-identity-token', # Required: Identity token from Sign in with Appleauthorization_code='apple-auth-code', # Optional: Authorization code for server verificationfull_name='John Doe'# Optional: User's name (only available on first sign-in)
)

Parameters:

ParameterTypeRequiredDescription
identity_tokenstrYesJWT identity token from Sign in with Apple
authorization_codestrNoAuthorization code for additional verification
full_namestrNoUser's full name (Apple only provides this on first sign-in)

Important Notes:

  • Apple only provides the user's name on the first sign-in. Store it immediately.
  • Users can choose to hide their email (Apple provides a relay email).
  • Required for apps using social login on iOS/macOS.
GitHub Sign-In

Authenticate users with their GitHub account. Popular for developer-focused applications.

# Sign in with GitHubuser=scs.auth.sign_in_with_github(
code='github-oauth-code', # Required: OAuth authorization coderedirect_uri='https://yourapp.com/callback'# Optional: Must match OAuth app settings
)
print(f"GitHub username: {user['displayName']}")
print(f"Email: {user['email']}")

Parameters:

ParameterTypeRequiredDescription
codestrYesOAuth authorization code from GitHub OAuth flow
redirect_uristrNoRedirect URI (must match your GitHub OAuth App settings)

OAuth Flow Example (Flask):

fromflaskimportFlask, redirect, requestimportrequestsapp=Flask(__name__)
GITHUB_CLIENT_ID='your-client-id'GITHUB_CLIENT_SECRET='your-client-secret'@app.route('/login/github')defgithub_login():
returnredirect(
f'https://github.com/login/oauth/authorize'f'?client_id={GITHUB_CLIENT_ID}'f'&redirect_uri=https://yourapp.com/callback'f'&scope=read:user user:email'
)
@app.route('/callback')defgithub_callback():
code=request.args.get('code')
ifcode:
user=scs.auth.sign_in_with_github(
code=code,
redirect_uri='https://yourapp.com/callback'
)
returnf"Welcome, {user['displayName']}!"return"Login failed"
Twitter/X Sign-In

Authenticate users with their Twitter/X account using OAuth 1.0a.

# Sign in with Twitter/Xuser=scs.auth.sign_in_with_twitter(
oauth_token='twitter-oauth-token', # Required: OAuth tokenoauth_token_secret='twitter-oauth-secret'# Required: OAuth token secret
)

Parameters:

ParameterTypeRequiredDescription
oauth_tokenstrYesOAuth token from Twitter authentication
oauth_token_secretstrYesOAuth token secret from Twitter authentication

Note: Twitter uses OAuth 1.0a which requires a more complex flow. Consider using a library like tweepy or python-twitter.

Microsoft Sign-In

Authenticate users with their Microsoft account (personal, work, or school accounts).

# Sign in with Microsoftuser=scs.auth.sign_in_with_microsoft(
access_token='microsoft-access-token', # Required: Access token from MSALid_token='microsoft-id-token'# Optional: ID token for additional claims
)

Parameters:

ParameterTypeRequiredDescription
access_tokenstrYesAccess token from Microsoft Authentication Library (MSAL)
id_tokenstrNoID token for additional user claims

Example with MSAL Python:

frommsalimportConfidentialClientApplicationapp=ConfidentialClientApplication(
client_id="your-client-id",
authority="https://login.microsoftonline.com/common",
client_credential="your-client-secret"
)
# After getting tokens from authorization code flowresult=app.acquire_token_by_authorization_code(
code,
scopes=["User.Read"],
redirect_uri="https://yourapp.com/callback"
)
if"access_token"inresult:
user=scs.auth.sign_in_with_microsoft(
access_token=result["access_token"],
id_token=result.get("id_token")
)

Anonymous Authentication

Allow users to use your app without creating an account. Anonymous accounts can later be upgraded to permanent accounts by linking a provider.

# Sign in anonymously (creates a temporary account)user=scs.auth.sign_in_anonymously(
custom_data={'referrer': 'landing-page', 'campaign': 'summer-sale'} # optional
)
print(f"Anonymous user ID: {user['uid']}")
print(f"Is anonymous: {user['isAnonymous']}") # True

Parameters:

ParameterTypeRequiredDescription
custom_datadictNoCustom data to store with the anonymous user for analytics

Use Cases:

  • Allow users to try your app before signing up
  • Guest checkout in e-commerce
  • Save user progress/preferences before account creation
  • A/B testing with user tracking

Converting Anonymous to Permanent Account:

# User decides to create a permanent account# Link their anonymous account to a providertry:
user=scs.auth.link_provider('google', {
'idToken': 'google-id-token'
})
print("Account upgraded! User data preserved.")
print(f"Is anonymous: {user['isAnonymous']}") # FalseexceptExceptionase:
if'credential-already-in-use'instr(e):
# This Google account is already linked to another userprint("This account is already registered. Please sign in instead.")
else:
raise

Phone Number Authentication

Two-step authentication flow using SMS verification codes.

# Step 1: Send verification code to phoneresult=scs.auth.send_phone_verification_code(
phone_number='+1234567890', # Required: E.164 formatrecaptcha_token='recaptcha-token'# Optional: For bot protection
)
verification_id=result['verificationId']
print(f"Verification ID: {verification_id}")
# Store this ID - you'll need it in step 2# Step 2: User enters the code they receiveduser=scs.auth.sign_in_with_phone_number(
verification_id=verification_id, # The ID from step 1code='123456'# 6-digit code from SMS
)
print(f"Phone verified: {user['phoneNumber']}")

send_phone_verification_code Parameters:

ParameterTypeRequiredDescription
phone_numberstrYesPhone number in E.164 format (e.g., +1234567890)
recaptcha_tokenstrNoreCAPTCHA token for abuse prevention

sign_in_with_phone_number Parameters:

ParameterTypeRequiredDescription
verification_idstrYesVerification ID from send_phone_verification_code
codestrYes6-digit verification code from SMS

Complete Flow Example:

defsign_in_with_phone(phone_number: str, get_code_callback) ->dict:
""" Sign in with phone number. Args: phone_number: Phone number in E.164 format get_code_callback: Function that prompts user for the SMS code Returns: User dict on success """try:
# Step 1: Send coderesult=scs.auth.send_phone_verification_code(
phone_number=phone_number
)
verification_id=result['verificationId']
# Get code from user (your UI implementation)code=get_code_callback()
# Step 2: Verify codeuser=scs.auth.sign_in_with_phone_number(
verification_id=verification_id,
code=code
)
returnuserexceptExceptionase:
error_msg=str(e).lower()
if'invalid-phone-number'inerror_msg:
raiseValueError('Invalid phone number format. Use E.164 format (+1234567890)')
elif'too-many-requests'inerror_msg:
raiseValueError('Too many attempts. Please try again later.')
elif'invalid-verification-code'inerror_msg:
raiseValueError('Invalid verification code. Please try again.')
elif'code-expired'inerror_msg:
raiseValueError('Code expired. Please request a new one.')
else:
raise

Custom Token Authentication

Sign in using a JWT token generated by your own backend. Useful for migrating users from another system or integrating with custom authentication.

# Sign in with a custom token (generated by your backend)user=scs.auth.sign_in_with_custom_token('your-custom-jwt-token')
print(f"Signed in user: {user['uid']}")

Parameters:

ParameterTypeRequiredDescription
tokenstrYesJWT token generated by your backend

Backend Token Generation Example:

importjwtimporttimeimportosdefcreate_custom_token(uid: str, claims: dict=None) ->str:
""" Create a custom authentication token. Args: uid: Unique user identifier claims: Optional custom claims to include Returns: JWT token string """payload= {
'uid': uid,
'claims': claimsor {},
'iat': int(time.time()),
'exp': int(time.time()) +3600# 1 hour
}
returnjwt.encode(
payload,
os.environ['SCS_SECRET_KEY'],
algorithm='HS256'
)
# Generate token for a usercustom_token=create_custom_token('user-123', {'role': 'admin'})
# Send this token to the client for sign-in

Use Cases:

  • Migrating users from another authentication system
  • Server-side user creation with immediate client sign-in
  • Integration with enterprise SSO systems (SAML, LDAP)
  • Machine-to-machine authentication

Account Linking

Link multiple authentication providers to a single account. Users can sign in with any linked provider.

# Link a provider to current accountuser=scs.auth.link_provider('facebook', {
'accessToken': 'facebook-access-token'
})
print(f"Linked providers: {user['providerData']}")
# [{'providerId': 'password'}, {'providerId': 'google'}, {'providerId': 'facebook'}]# Unlink a provider from current accountuser=scs.auth.unlink_provider('facebook')
print(f"Remaining providers: {user['providerData']}")
# Get available sign-in methods for an emailresult=scs.auth.fetch_sign_in_methods_for_email('user@example.com')
print(f"Available methods: {result['methods']}")
# ['password', 'google', 'facebook']

link_provider Parameters:

ParameterTypeRequiredDescription
providerstrYesProvider name: 'google', 'facebook', 'apple', 'github', 'twitter', 'microsoft'
credentialsdictYesProvider-specific credentials (tokens)

Supported Providers and Credentials:

ProviderRequired Credentials
google{'idToken': '...', 'accessToken': '...'} (accessToken optional)
facebook{'accessToken': '...'}
apple{'identityToken': '...', 'authorizationCode': '...', 'fullName': '...'}
github{'code': '...', 'redirectUri': '...'}
twitter{'oauthToken': '...', 'oauthTokenSecret': '...'}
microsoft{'accessToken': '...', 'idToken': '...'}

Complete Account Linking Flow:

defcan_link_provider(email: str, provider: str) ->bool:
"""Check if a provider can be linked to an account."""result=scs.auth.fetch_sign_in_methods_for_email(email)
methods=result.get('methods', [])
ifproviderinmethods:
raiseValueError(f"{provider} is already linked to this account")
returnTruedeflink_google_account(google_id_token: str) ->dict:
"""Link Google to the current user's account."""try:
# Verify user is signed incurrent_user=scs.auth.get_current_user()
ifnotcurrent_user:
raiseValueError('Must be signed in to link accounts')
# Link the provideruser=scs.auth.link_provider('google', {
'idToken': google_id_token
})
print('Successfully linked Google account')
returnuserexceptExceptionase:
error_msg=str(e).lower()
if'credential-already-in-use'inerror_msg:
print('This Google account is already linked to another user')
elif'provider-already-linked'inerror_msg:
print('Google is already linked to this account')
raise

Password Reset & Email Verification

Handle password recovery and email verification flows.

# Send password reset emailscs.auth.send_password_reset_email('user@example.com')
print("Password reset email sent")
# Confirm password reset (user clicks link in email, you extract the code)scs.auth.confirm_password_reset(
code='reset-code-from-email', # Code from the reset linknew_password='newSecurePassword123'# User's new password
)
print("Password successfully reset")
# Send email verification to current userscs.auth.send_email_verification()
print("Verification email sent")
# Verify email with code (user clicks link, you extract the code)scs.auth.verify_email('verification-code')
print("Email verified")

Error Handling:

defreset_password(email: str) ->dict:
"""Send password reset email with error handling."""try:
scs.auth.send_password_reset_email(email)
return {'success': True, 'message': 'Reset email sent'}
exceptExceptionase:
error_msg=str(e).lower()
if'user-not-found'inerror_msg:
# Don't reveal if user exists for securityreturn {'success': True, 'message': 'If this email exists, a reset link was sent'}
elif'too-many-requests'inerror_msg:
return {'success': False, 'message': 'Too many attempts. Please try later.'}
else:
raisedefconfirm_reset(code: str, new_password: str) ->dict:
"""Confirm password reset with validation."""# Validate password strengthiflen(new_password) <8:
raiseValueError('Password must be at least 8 characters')
try:
scs.auth.confirm_password_reset(code=code, new_password=new_password)
return {'success': True}
exceptExceptionase:
error_msg=str(e).lower()
if'expired-action-code'inerror_msg:
return {'success': False, 'message': 'Reset link expired. Please request a new one.'}
elif'invalid-action-code'inerror_msg:
return {'success': False, 'message': 'Invalid reset link.'}
elif'weak-password'inerror_msg:
return {'success': False, 'message': 'Password is too weak.'}
else:
raise

Authentication State Management

Manage user sessions and authentication state.

# Check if user is logged inis_logged_in=scs.auth.is_logged_inprint(f"Is logged in: {is_logged_in}")
# Get current user (from cache)cached_user=scs.auth.current_user# Get current user (fresh from server)user=scs.auth.get_current_user()
# Refresh user datarefreshed_user=scs.auth.reload()
# Get the current auth tokentoken=scs.auth.token

Complete Authentication Example

fromscsimportSCS, AuthenticationErrorclassAuthService:
def__init__(self):
self.scs=SCS({
'api_key': 'your-api-key',
'project_id': 'your-project-id',
'base_url': 'https://your-scs-instance.com'
})
defregister(self, email: str, password: str, display_name: str) ->dict:
"""Register a new user with email verification."""user=self.scs.auth.register(
email=email,
password=password,
display_name=display_name
)
# Send verification emailself.scs.auth.send_email_verification()
returnuserdeflogin(self, email: str, password: str) ->dict:
"""Login with email and password."""returnself.scs.auth.login(email=email, password=password)
defsocial_login(self, provider: str, credentials: dict) ->dict:
"""Login with any social provider."""methods= {
'google': lambda: self.scs.auth.sign_in_with_google(**credentials),
'facebook': lambda: self.scs.auth.sign_in_with_facebook(**credentials),
'apple': lambda: self.scs.auth.sign_in_with_apple(**credentials),
'github': lambda: self.scs.auth.sign_in_with_github(**credentials),
'twitter': lambda: self.scs.auth.sign_in_with_twitter(**credentials),
'microsoft': lambda: self.scs.auth.sign_in_with_microsoft(**credentials)
}
ifprovidernotinmethods:
raiseValueError(f"Unknown provider: {provider}")
returnmethods[provider]()
defcontinue_as_guest(self, custom_data: dict=None) ->dict:
"""Sign in anonymously for guest access."""returnself.scs.auth.sign_in_anonymously(custom_data=custom_dataor {})
defupgrade_guest_account(self, provider: str, credentials: dict) ->dict:
"""Upgrade anonymous account to permanent."""user=self.scs.auth.get_current_user()
ifnotuserornotuser.get('isAnonymous'):
raiseValueError('Current user is not anonymous')
returnself.scs.auth.link_provider(provider, credentials)
deflogout(self):
"""Sign out the current user."""self.scs.auth.logout()
# Usageauth=AuthService()
# Register new useruser=auth.register('user@example.com', 'password123', 'John Doe')
# Or sign in with Googlegoogle_user=auth.social_login('google', {'id_token': 'xxx'})
# Or continue as guest and upgrade laterguest=auth.continue_as_guest({'source': 'homepage'})
# ... user decides to create account ...upgraded=auth.upgrade_guest_account('google', {'idToken': 'xxx'})

Database

Document database with query builder. SCS supports two powerful database options:

Database Types

TypeNameDescriptionBest For
eazieaZI DatabaseDocument-based NoSQL with Firestore-like collections, documents, and subcollectionsDevelopment, prototyping, small to medium apps
reladbRelaDBProduction-grade NoSQL database with relational-style viewsProduction, scalability, advanced queries

Initialize with eaZI (Default)

fromscsimportSCS# eaZI is the default database - no special configuration neededscs=SCS({
'project_id': 'your-project-id',
'api_key': 'your-api-key'# database_type: 'eazi' is implicit
})

Initialize with RelaDB (Production)

fromscsimportSCS# Use RelaDB for productionscs=SCS({
'project_id': 'your-project-id',
'api_key': 'your-api-key',
'database_type': 'reladb'# Enable RelaDB
})

eaZI Database Features

  • Document-based: Firestore-like collections and documents
  • Subcollections: Nested data organization
  • File-based storage: No external dependencies required
  • Zero configuration: Works out of the box
  • Query support: Filtering, ordering, and pagination

RelaDB Features

  • Production-ready: Built for reliability and performance
  • Scalable: Horizontal scaling and replication support
  • Advanced queries: Aggregation pipelines, complex filters
  • Indexing: Custom indexes for optimized performance
  • Schema flexibility: Dynamic schema with validation support
  • Relational-style views: Table view with columns and rows in the console

Collection Operations

# Get a collection referenceusers=scs.database.collection('users')
# List all collectionscollections=scs.database.list_collections()
# Create a collectionscs.database.create_collection('new_collection')
# Delete a collectionscs.database.delete_collection('old_collection')

Document Operations

# Add document with auto-generated IDdoc=scs.database.collection('users').add({
'name': 'John Doe',
'email': 'john@example.com',
'age': 30,
'tags': ['developer', 'python'],
'profile': {
'bio': 'Software developer',
'avatar': 'https://example.com/avatar.jpg'
}
})
print(f'Document ID: {doc["id"]}')
# Set document with custom ID (creates or overwrites)scs.database.collection('users').doc('user-123').set({
'name': 'Jane Doe',
'email': 'jane@example.com'
})
# Get a single documentuser=scs.database.collection('users').doc('user-123').get()
print(user)
# Update document (partial update)scs.database.collection('users').doc('user-123').update({
'age': 31,
'profile.bio': 'Senior developer'
})
# Delete documentscs.database.collection('users').doc('user-123').delete()

Query Operations

# Simple query with single filteractive_users=scs.database.collection('users') \
.where('status', '==', 'active') \
.get()
# Multiple filtersresults=scs.database.collection('users') \
.where('age', '>=', 18) \
.where('status', '==', 'active') \
.get()
# Ordering and paginationposts=scs.database.collection('posts') \
.where('published', '==', True) \
.order_by('created_at', 'desc') \
.limit(10) \
.skip(20) \
.get()
# Using 'in' operatorfeatured=scs.database.collection('posts') \
.where('category', 'in', ['tech', 'science', 'news']) \
.get()
# Using 'contains' for array fieldstagged=scs.database.collection('posts') \
.where('tags', 'contains', 'python') \
.get()

Query Operators

OperatorDescriptionExample
==Equal to.where('status', '==', 'active')
!=Not equal to.where('status', '!=', 'deleted')
>Greater than.where('age', '>', 18)
>=Greater than or equal.where('age', '>=', 18)
<Less than.where('price', '<', 100)
<=Less than or equal.where('price', '<=', 50)
inValue in array.where('status', 'in', ['active', 'pending'])
containsArray contains value.where('tags', 'contains', 'featured')

Subcollections

# Access a subcollectionposts_ref=scs.database \
.collection('users') \
.doc('user_id') \
.collection('posts')
# Add to subcollectionpost=posts_ref.add({
'title': 'My First Post',
'content': 'Hello World!',
'created_at': datetime.now().isoformat()
})
# Query subcollectionuser_posts=posts_ref \
.order_by('created_at', 'desc') \
.limit(5) \
.get()
# Nested subcollections (e.g., users/user_id/posts/post_id/comments)comments_ref=scs.database \
.collection('users') \
.doc('user_id') \
.collection('posts') \
.doc('post_id') \
.collection('comments')

Storage

# Upload from file pathfile_info=scs.storage.upload('./image.png')
# Upload with folderfile_info=scs.storage.upload('./doc.pdf', folder='documents')
# Upload from bytesfile_info=scs.storage.upload_bytes(
data=image_bytes,
filename='image.png',
content_type='image/png'
)
# List filesfiles=scs.storage.list_files(folder='images')
# Get file referencefile_ref=scs.storage.ref('file-id')
# Download filecontent=file_ref.download()
# Download to filefile_ref.download_to_file('./local_file.png')
# Get metadatametadata=file_ref.get_metadata()
# Delete filefile_ref.delete()
# Folder operationsscs.storage.create_folder('my-folder')
scs.storage.delete_folder('my-folder')

Realtime Database

Real-time data synchronization via WebSocket:

# Connect to realtime servicescs.realtime.connect()
# Get referencechat_ref=scs.realtime.ref('chat/room1')
# Subscribe to updatesdefon_message(data, event):
print(f'Event: {event}, Data: {data}')
unsubscribe=chat_ref.on(on_message)
# Subscribe to single updatechat_ref.once(lambdadata, event: print(data))
# Unsubscribeunsubscribe()
# Disconnectscs.realtime.disconnect()

Cloud Messaging

Push notifications with topics and direct messaging:

# Register device tokenscs.messaging.register_token(
token='device-fcm-token',
platform='android'
)
# Topic operationsscs.messaging.create_topic('news', description='News updates')
scs.messaging.subscribe_to_topic('news', 'device-token')
scs.messaging.unsubscribe_from_topic('news', 'device-token')
# Send to topicscs.messaging.send_to_topic(
topic='news',
title='Breaking News',
body='Something happened!',
data={'articleId': '123'}
)
# Send to specific devicescs.messaging.send_to_token(
token='device-token',
title='Personal Alert',
body='You have a new message'
)
# Send to multiple devicesscs.messaging.send_to_tokens(
tokens=['token1', 'token2'],
title='Broadcast',
body='Hello everyone!'
)

Remote Config

Dynamic app configuration:

# Fetch configurationconfig=scs.remote_config.fetch()
# Get values with type safetytheme=scs.remote_config.get_string('app_theme', default='light')
max_items=scs.remote_config.get_int('max_items', default=10)
enabled=scs.remote_config.get_bool('feature_enabled', default=False)
settings=scs.remote_config.get_json('settings', default={})
# Admin: Manage parametersscs.remote_config.create_param(
key='app_theme',
value='dark',
description='Default app theme'
)
scs.remote_config.update_param('app_theme', 'light')
scs.remote_config.delete_param('old_param')
# Publish changesscs.remote_config.publish()
# Version managementversions=scs.remote_config.list_versions()
scs.remote_config.rollback('version-id')

Serverless Functions

Execute custom code on the backend:

# Invoke a functionresult=scs.functions.invoke('processOrder', {
'orderId': '12345',
'action': 'confirm'
})
# Admin: Create a functionscs.functions.create(
name='processOrder',
code=''' module.exports = async (data, context) => { const { orderId, action } = data; // Process order... return { success: true, orderId }; }; ''',
runtime='nodejs18',
timeout=30000,
memory=256
)
# Admin: List functionsfunctions=scs.functions.list()
# Admin: Update functionscs.functions.update('function-id', code='...')
# Admin: Test functionresult=scs.functions.test('function-id', {'test': 'data'})
# Admin: Get logslogs=scs.functions.get_logs('function-id')

AI

Chat, text completion, and image generation with local LLM models:

# Chat with AIresponse=scs.ai.chat(
message="What is the capital of France?",
system_prompt="You are a helpful geography assistant."
)
print(response['content'])
# Text completionresponse=scs.ai.complete(prompt="Once upon a time")
print(response['content'])
# Generate imageresponse=scs.ai.generate_image(prompt="A sunset over mountains")
print(response['imageUrl'])
# List available modelsmodels=scs.ai.list_models()
# Conversation managementconversation=scs.ai.create_conversation(title="Geography Chat")
scs.ai.get_conversation(conversation['conversationId'])
scs.ai.delete_conversation(conversation['conversationId'])

AI Agents

Create and manage AI agents with custom instructions and tools:

# Create an agentagent=scs.ai.create_agent(
name="Customer Support",
instructions="You are a helpful customer support assistant. Be polite and helpful.",
model="llama3.2",
temperature=0.7
)
print(f"Created agent: {agent['agentId']}")
# List agentsagents=scs.ai.list_agents()
# Run the agentresponse=scs.ai.run_agent(
agent_id=agent['agentId'],
input="How do I reset my password?"
)
print(f"Agent: {response['output']}")
print(f"Session: {response['sessionId']}")
# Continue the conversation in the same sessionresponse=scs.ai.run_agent(
agent_id=agent['agentId'],
input="Thanks! What about enabling 2FA?",
session_id=response['sessionId']
)
# List agent sessionssessions=scs.ai.list_agent_sessions(agent['agentId'])
# Get full session historysession=scs.ai.get_agent_session(agent['agentId'], response['sessionId'])
formsginsession['messages']:
print(f"{msg['role']}: {msg['content']}")
# Update agentscs.ai.update_agent(
agent_id=agent['agentId'],
instructions="Updated instructions here",
temperature=0.5
)
# Define a tool for agentstool=scs.ai.define_tool(
name="get_weather",
description="Get weather for a location",
parameters={
"type": "object",
"properties": {
"location": {"type": "string", "description": "City name"}
}
}
)
# List toolstools=scs.ai.list_tools()
# Delete agent and sessionsscs.ai.delete_agent_session(agent['agentId'], response['sessionId'])
scs.ai.delete_agent(agent['agentId'])

Configuration File

Create an scs-info.json file:

{
"sdk_config": {
"api_key": "pk_your_api_key",
"project_id": "your-project-id",
"base_url": "https://your-scs-instance.com"
}
}

Error Handling

fromscsimportSCS, SCSError, AuthenticationError, NotFoundError, ValidationErrortry:
user=scs.auth.login(email='user@example.com', password='wrong')
exceptAuthenticationErrorase:
print(f'Auth failed: {e.message}')
print(f'Status: {e.status}')
exceptNotFoundErrorase:
print(f'Not found: {e.message}')
exceptValidationErrorase:
print(f'Validation error: {e.message}')
exceptSCSErrorase:
print(f'SCS error: {e.message}')

Context Manager

withSCS({'api_key': '...', 'project_id': '...'}) asscs:
users=scs.database.collection('users').get()
# Connection automatically closed

Requirements

  • Python 3.8+
  • requests
  • python-socketio[client] (optional, for realtime functionality)

License

MIT License

About

The official Python SDK for Spyxpo Cloud Services

Resources

Code of conduct

Contributing

Security policy

Stars

2 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Latest commit

History

13 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

SCS Python SDK

Python SDK for SCS (Spyxpo Cloud Services)

Installation

pip install scs-sdk

For realtime functionality (WebSocket support):

pip install scs-sdk[realtime]

Quick Start

fromscsimportSCS# Initialize from config filescs=SCS.initialize_app('./scs-info.json')
# Or initialize with config dictscs=SCS({
'api_key': 'your-api-key',
'project_id': 'your-project-id',
'base_url': 'https://your-scs-instance.com'
})
# Authenticationuser=scs.auth.login(email='user@example.com', password='password123')
# Databaseusers=scs.database.collection('users').where('age', '>=', 18).limit(10).get()
# Storagescs.storage.upload('./image.png', folder='images')

Services

Authentication

# Register a new useruser=scs.auth.register(
email='user@example.com',
password='password123',
display_name='John Doe'
)
# Loginuser=scs.auth.login(email='user@example.com', password='password123')
# Get current userme=scs.auth.get_current_user()
# Update profilescs.auth.update_profile(display_name='Jane Doe')
# Change passwordscs.auth.change_password(
current_password='old123',
new_password='new456'
)
# Logoutscs.auth.logout()

OAuth and Social Sign-In

SCS supports multiple OAuth providers for seamless social authentication. Each provider follows a similar pattern but requires different credentials obtained from their respective SDKs.

Google Sign-In

Authenticate users with their Google account. Requires the Google Sign-In SDK on the client.

# Sign in with Googleuser=scs.auth.sign_in_with_google(
id_token='google-id-token', # Required: ID token from Google Sign-Inaccess_token='google-access-token'# Optional: Access token for additional scopes
)
print(f"User ID: {user['uid']}")
print(f"Email: {user['email']}")
print(f"Display Name: {user['displayName']}")
print(f"Photo URL: {user['photoURL']}")
print(f"Provider: {user['providerId']}") # 'google'

Parameters:

ParameterTypeRequiredDescription
id_tokenstrYesThe ID token obtained from Google Sign-In SDK
access_tokenstrNoAccess token for additional Google API scopes

Returns:dict with user data and token

Example with Google OAuth Library:

fromgoogle.oauth2importid_tokenfromgoogle.auth.transportimportrequests# Verify and get token on server sidedefverify_google_token(token):
try:
idinfo=id_token.verify_oauth2_token(
token, requests.Request(), GOOGLE_CLIENT_ID
)
returnidinfoexceptValueError:
returnNone# Sign in with the tokenuser=scs.auth.sign_in_with_google(id_token=google_token)
Facebook Sign-In

Authenticate users with their Facebook account.

# Sign in with Facebookuser=scs.auth.sign_in_with_facebook(
access_token='facebook-access-token'# Required: Access token from Facebook Login
)
print(f"User: {user['displayName']}")
print(f"Email: {user['email']}") # May be None if user didn't grant email permission

Parameters:

ParameterTypeRequiredDescription
access_tokenstrYesAccess token from Facebook Login SDK

Example with Facebook SDK:

# After getting access token from Facebook SDK on clientdeffacebook_login(access_token):
try:
user=scs.auth.sign_in_with_facebook(access_token=access_token)
returnuserexceptExceptionase:
print(f"Facebook login failed: {e}")
returnNone
Apple Sign-In

Authenticate users with their Apple ID. Ideal for iOS apps and required for apps with social login on the App Store.

# Sign in with Appleuser=scs.auth.sign_in_with_apple(
identity_token='apple-identity-token', # Required: Identity token from Sign in with Appleauthorization_code='apple-auth-code', # Optional: Authorization code for server verificationfull_name='John Doe'# Optional: User's name (only available on first sign-in)
)

Parameters:

ParameterTypeRequiredDescription
identity_tokenstrYesJWT identity token from Sign in with Apple
authorization_codestrNoAuthorization code for additional verification
full_namestrNoUser's full name (Apple only provides this on first sign-in)

Important Notes:

  • Apple only provides the user's name on the first sign-in. Store it immediately.
  • Users can choose to hide their email (Apple provides a relay email).
  • Required for apps using social login on iOS/macOS.
GitHub Sign-In

Authenticate users with their GitHub account. Popular for developer-focused applications.

# Sign in with GitHubuser=scs.auth.sign_in_with_github(
code='github-oauth-code', # Required: OAuth authorization coderedirect_uri='https://yourapp.com/callback'# Optional: Must match OAuth app settings
)
print(f"GitHub username: {user['displayName']}")
print(f"Email: {user['email']}")

Parameters:

ParameterTypeRequiredDescription
codestrYesOAuth authorization code from GitHub OAuth flow
redirect_uristrNoRedirect URI (must match your GitHub OAuth App settings)

OAuth Flow Example (Flask):

fromflaskimportFlask, redirect, requestimportrequestsapp=Flask(__name__)
GITHUB_CLIENT_ID='your-client-id'GITHUB_CLIENT_SECRET='your-client-secret'@app.route('/login/github')defgithub_login():
returnredirect(
f'https://github.com/login/oauth/authorize'f'?client_id={GITHUB_CLIENT_ID}'f'&redirect_uri=https://yourapp.com/callback'f'&scope=read:user user:email'
)
@app.route('/callback')defgithub_callback():
code=request.args.get('code')
ifcode:
user=scs.auth.sign_in_with_github(
code=code,
redirect_uri='https://yourapp.com/callback'
)
returnf"Welcome, {user['displayName']}!"return"Login failed"
Twitter/X Sign-In

Authenticate users with their Twitter/X account using OAuth 1.0a.

# Sign in with Twitter/Xuser=scs.auth.sign_in_with_twitter(
oauth_token='twitter-oauth-token', # Required: OAuth tokenoauth_token_secret='twitter-oauth-secret'# Required: OAuth token secret
)

Parameters:

ParameterTypeRequiredDescription
oauth_tokenstrYesOAuth token from Twitter authentication
oauth_token_secretstrYesOAuth token secret from Twitter authentication

Note: Twitter uses OAuth 1.0a which requires a more complex flow. Consider using a library like tweepy or python-twitter.

Microsoft Sign-In

Authenticate users with their Microsoft account (personal, work, or school accounts).

# Sign in with Microsoftuser=scs.auth.sign_in_with_microsoft(
access_token='microsoft-access-token', # Required: Access token from MSALid_token='microsoft-id-token'# Optional: ID token for additional claims
)

Parameters:

ParameterTypeRequiredDescription
access_tokenstrYesAccess token from Microsoft Authentication Library (MSAL)
id_tokenstrNoID token for additional user claims

Example with MSAL Python:

frommsalimportConfidentialClientApplicationapp=ConfidentialClientApplication(
client_id="your-client-id",
authority="https://login.microsoftonline.com/common",
client_credential="your-client-secret"
)
# After getting tokens from authorization code flowresult=app.acquire_token_by_authorization_code(
code,
scopes=["User.Read"],
redirect_uri="https://yourapp.com/callback"
)
if"access_token"inresult:
user=scs.auth.sign_in_with_microsoft(
access_token=result["access_token"],
id_token=result.get("id_token")
)

Anonymous Authentication

Allow users to use your app without creating an account. Anonymous accounts can later be upgraded to permanent accounts by linking a provider.

# Sign in anonymously (creates a temporary account)user=scs.auth.sign_in_anonymously(
custom_data={'referrer': 'landing-page', 'campaign': 'summer-sale'} # optional
)
print(f"Anonymous user ID: {user['uid']}")
print(f"Is anonymous: {user['isAnonymous']}") # True

Parameters:

ParameterTypeRequiredDescription
custom_datadictNoCustom data to store with the anonymous user for analytics

Use Cases:

  • Allow users to try your app before signing up
  • Guest checkout in e-commerce
  • Save user progress/preferences before account creation
  • A/B testing with user tracking

Converting Anonymous to Permanent Account:

# User decides to create a permanent account# Link their anonymous account to a providertry:
user=scs.auth.link_provider('google', {
'idToken': 'google-id-token'
})
print("Account upgraded! User data preserved.")
print(f"Is anonymous: {user['isAnonymous']}") # FalseexceptExceptionase:
if'credential-already-in-use'instr(e):
# This Google account is already linked to another userprint("This account is already registered. Please sign in instead.")
else:
raise

Phone Number Authentication

Two-step authentication flow using SMS verification codes.

# Step 1: Send verification code to phoneresult=scs.auth.send_phone_verification_code(
phone_number='+1234567890', # Required: E.164 formatrecaptcha_token='recaptcha-token'# Optional: For bot protection
)
verification_id=result['verificationId']
print(f"Verification ID: {verification_id}")
# Store this ID - you'll need it in step 2# Step 2: User enters the code they receiveduser=scs.auth.sign_in_with_phone_number(
verification_id=verification_id, # The ID from step 1code='123456'# 6-digit code from SMS
)
print(f"Phone verified: {user['phoneNumber']}")

send_phone_verification_code Parameters:

ParameterTypeRequiredDescription
phone_numberstrYesPhone number in E.164 format (e.g., +1234567890)
recaptcha_tokenstrNoreCAPTCHA token for abuse prevention

sign_in_with_phone_number Parameters:

ParameterTypeRequiredDescription
verification_idstrYesVerification ID from send_phone_verification_code
codestrYes6-digit verification code from SMS

Complete Flow Example:

defsign_in_with_phone(phone_number: str, get_code_callback) ->dict:
""" Sign in with phone number. Args: phone_number: Phone number in E.164 format get_code_callback: Function that prompts user for the SMS code Returns: User dict on success """try:
# Step 1: Send coderesult=scs.auth.send_phone_verification_code(
phone_number=phone_number
)
verification_id=result['verificationId']
# Get code from user (your UI implementation)code=get_code_callback()
# Step 2: Verify codeuser=scs.auth.sign_in_with_phone_number(
verification_id=verification_id,
code=code
)
returnuserexceptExceptionase:
error_msg=str(e).lower()
if'invalid-phone-number'inerror_msg:
raiseValueError('Invalid phone number format. Use E.164 format (+1234567890)')
elif'too-many-requests'inerror_msg:
raiseValueError('Too many attempts. Please try again later.')
elif'invalid-verification-code'inerror_msg:
raiseValueError('Invalid verification code. Please try again.')
elif'code-expired'inerror_msg:
raiseValueError('Code expired. Please request a new one.')
else:
raise

Custom Token Authentication

Sign in using a JWT token generated by your own backend. Useful for migrating users from another system or integrating with custom authentication.

# Sign in with a custom token (generated by your backend)user=scs.auth.sign_in_with_custom_token('your-custom-jwt-token')
print(f"Signed in user: {user['uid']}")

Parameters:

ParameterTypeRequiredDescription
tokenstrYesJWT token generated by your backend

Backend Token Generation Example:

importjwtimporttimeimportosdefcreate_custom_token(uid: str, claims: dict=None) ->str:
""" Create a custom authentication token. Args: uid: Unique user identifier claims: Optional custom claims to include Returns: JWT token string """payload= {
'uid': uid,
'claims': claimsor {},
'iat': int(time.time()),
'exp': int(time.time()) +3600# 1 hour
}
returnjwt.encode(
payload,
os.environ['SCS_SECRET_KEY'],
algorithm='HS256'
)
# Generate token for a usercustom_token=create_custom_token('user-123', {'role': 'admin'})
# Send this token to the client for sign-in

Use Cases:

  • Migrating users from another authentication system
  • Server-side user creation with immediate client sign-in
  • Integration with enterprise SSO systems (SAML, LDAP)
  • Machine-to-machine authentication

Account Linking

Link multiple authentication providers to a single account. Users can sign in with any linked provider.

# Link a provider to current accountuser=scs.auth.link_provider('facebook', {
'accessToken': 'facebook-access-token'
})
print(f"Linked providers: {user['providerData']}")
# [{'providerId': 'password'}, {'providerId': 'google'}, {'providerId': 'facebook'}]# Unlink a provider from current accountuser=scs.auth.unlink_provider('facebook')
print(f"Remaining providers: {user['providerData']}")
# Get available sign-in methods for an emailresult=scs.auth.fetch_sign_in_methods_for_email('user@example.com')
print(f"Available methods: {result['methods']}")
# ['password', 'google', 'facebook']

link_provider Parameters:

ParameterTypeRequiredDescription
providerstrYesProvider name: 'google', 'facebook', 'apple', 'github', 'twitter', 'microsoft'
credentialsdictYesProvider-specific credentials (tokens)

Supported Providers and Credentials:

ProviderRequired Credentials
google{'idToken': '...', 'accessToken': '...'} (accessToken optional)
facebook{'accessToken': '...'}
apple{'identityToken': '...', 'authorizationCode': '...', 'fullName': '...'}
github{'code': '...', 'redirectUri': '...'}
twitter{'oauthToken': '...', 'oauthTokenSecret': '...'}
microsoft{'accessToken': '...', 'idToken': '...'}

Complete Account Linking Flow:

defcan_link_provider(email: str, provider: str) ->bool:
"""Check if a provider can be linked to an account."""result=scs.auth.fetch_sign_in_methods_for_email(email)
methods=result.get('methods', [])
ifproviderinmethods:
raiseValueError(f"{provider} is already linked to this account")
returnTruedeflink_google_account(google_id_token: str) ->dict:
"""Link Google to the current user's account."""try:
# Verify user is signed incurrent_user=scs.auth.get_current_user()
ifnotcurrent_user:
raiseValueError('Must be signed in to link accounts')
# Link the provideruser=scs.auth.link_provider('google', {
'idToken': google_id_token
})
print('Successfully linked Google account')
returnuserexceptExceptionase:
error_msg=str(e).lower()
if'credential-already-in-use'inerror_msg:
print('This Google account is already linked to another user')
elif'provider-already-linked'inerror_msg:
print('Google is already linked to this account')
raise

Password Reset & Email Verification

Handle password recovery and email verification flows.

# Send password reset emailscs.auth.send_password_reset_email('user@example.com')
print("Password reset email sent")
# Confirm password reset (user clicks link in email, you extract the code)scs.auth.confirm_password_reset(
code='reset-code-from-email', # Code from the reset linknew_password='newSecurePassword123'# User's new password
)
print("Password successfully reset")
# Send email verification to current userscs.auth.send_email_verification()
print("Verification email sent")
# Verify email with code (user clicks link, you extract the code)scs.auth.verify_email('verification-code')
print("Email verified")

Error Handling:

defreset_password(email: str) ->dict:
"""Send password reset email with error handling."""try:
scs.auth.send_password_reset_email(email)
return {'success': True, 'message': 'Reset email sent'}
exceptExceptionase:
error_msg=str(e).lower()
if'user-not-found'inerror_msg:
# Don't reveal if user exists for securityreturn {'success': True, 'message': 'If this email exists, a reset link was sent'}
elif'too-many-requests'inerror_msg:
return {'success': False, 'message': 'Too many attempts. Please try later.'}
else:
raisedefconfirm_reset(code: str, new_password: str) ->dict:
"""Confirm password reset with validation."""# Validate password strengthiflen(new_password) <8:
raiseValueError('Password must be at least 8 characters')
try:
scs.auth.confirm_password_reset(code=code, new_password=new_password)
return {'success': True}
exceptExceptionase:
error_msg=str(e).lower()
if'expired-action-code'inerror_msg:
return {'success': False, 'message': 'Reset link expired. Please request a new one.'}
elif'invalid-action-code'inerror_msg:
return {'success': False, 'message': 'Invalid reset link.'}
elif'weak-password'inerror_msg:
return {'success': False, 'message': 'Password is too weak.'}
else:
raise

Authentication State Management

Manage user sessions and authentication state.

# Check if user is logged inis_logged_in=scs.auth.is_logged_inprint(f"Is logged in: {is_logged_in}")
# Get current user (from cache)cached_user=scs.auth.current_user# Get current user (fresh from server)user=scs.auth.get_current_user()
# Refresh user datarefreshed_user=scs.auth.reload()
# Get the current auth tokentoken=scs.auth.token

Complete Authentication Example

fromscsimportSCS, AuthenticationErrorclassAuthService:
def__init__(self):
self.scs=SCS({
'api_key': 'your-api-key',
'project_id': 'your-project-id',
'base_url': 'https://your-scs-instance.com'
})
defregister(self, email: str, password: str, display_name: str) ->dict:
"""Register a new user with email verification."""user=self.scs.auth.register(
email=email,
password=password,
display_name=display_name
)
# Send verification emailself.scs.auth.send_email_verification()
returnuserdeflogin(self, email: str, password: str) ->dict:
"""Login with email and password."""returnself.scs.auth.login(email=email, password=password)
defsocial_login(self, provider: str, credentials: dict) ->dict:
"""Login with any social provider."""methods= {
'google': lambda: self.scs.auth.sign_in_with_google(**credentials),
'facebook': lambda: self.scs.auth.sign_in_with_facebook(**credentials),
'apple': lambda: self.scs.auth.sign_in_with_apple(**credentials),
'github': lambda: self.scs.auth.sign_in_with_github(**credentials),
'twitter': lambda: self.scs.auth.sign_in_with_twitter(**credentials),
'microsoft': lambda: self.scs.auth.sign_in_with_microsoft(**credentials)
}
ifprovidernotinmethods:
raiseValueError(f"Unknown provider: {provider}")
returnmethods[provider]()
defcontinue_as_guest(self, custom_data: dict=None) ->dict:
"""Sign in anonymously for guest access."""returnself.scs.auth.sign_in_anonymously(custom_data=custom_dataor {})
defupgrade_guest_account(self, provider: str, credentials: dict) ->dict:
"""Upgrade anonymous account to permanent."""user=self.scs.auth.get_current_user()
ifnotuserornotuser.get('isAnonymous'):
raiseValueError('Current user is not anonymous')
returnself.scs.auth.link_provider(provider, credentials)
deflogout(self):
"""Sign out the current user."""self.scs.auth.logout()
# Usageauth=AuthService()
# Register new useruser=auth.register('user@example.com', 'password123', 'John Doe')
# Or sign in with Googlegoogle_user=auth.social_login('google', {'id_token': 'xxx'})
# Or continue as guest and upgrade laterguest=auth.continue_as_guest({'source': 'homepage'})
# ... user decides to create account ...upgraded=auth.upgrade_guest_account('google', {'idToken': 'xxx'})

Database

Document database with query builder. SCS supports two powerful database options:

Database Types

TypeNameDescriptionBest For
eazieaZI DatabaseDocument-based NoSQL with Firestore-like collections, documents, and subcollectionsDevelopment, prototyping, small to medium apps
reladbRelaDBProduction-grade NoSQL database with relational-style viewsProduction, scalability, advanced queries

Initialize with eaZI (Default)

fromscsimportSCS# eaZI is the default database - no special configuration neededscs=SCS({
'project_id': 'your-project-id',
'api_key': 'your-api-key'# database_type: 'eazi' is implicit
})

Initialize with RelaDB (Production)

fromscsimportSCS# Use RelaDB for productionscs=SCS({
'project_id': 'your-project-id',
'api_key': 'your-api-key',
'database_type': 'reladb'# Enable RelaDB
})

eaZI Database Features

  • Document-based: Firestore-like collections and documents
  • Subcollections: Nested data organization
  • File-based storage: No external dependencies required
  • Zero configuration: Works out of the box
  • Query support: Filtering, ordering, and pagination

RelaDB Features

  • Production-ready: Built for reliability and performance
  • Scalable: Horizontal scaling and replication support
  • Advanced queries: Aggregation pipelines, complex filters
  • Indexing: Custom indexes for optimized performance
  • Schema flexibility: Dynamic schema with validation support
  • Relational-style views: Table view with columns and rows in the console

Collection Operations

# Get a collection referenceusers=scs.database.collection('users')
# List all collectionscollections=scs.database.list_collections()
# Create a collectionscs.database.create_collection('new_collection')
# Delete a collectionscs.database.delete_collection('old_collection')

Document Operations

# Add document with auto-generated IDdoc=scs.database.collection('users').add({
'name': 'John Doe',
'email': 'john@example.com',
'age': 30,
'tags': ['developer', 'python'],
'profile': {
'bio': 'Software developer',
'avatar': 'https://example.com/avatar.jpg'
}
})
print(f'Document ID: {doc["id"]}')
# Set document with custom ID (creates or overwrites)scs.database.collection('users').doc('user-123').set({
'name': 'Jane Doe',
'email': 'jane@example.com'
})
# Get a single documentuser=scs.database.collection('users').doc('user-123').get()
print(user)
# Update document (partial update)scs.database.collection('users').doc('user-123').update({
'age': 31,
'profile.bio': 'Senior developer'
})
# Delete documentscs.database.collection('users').doc('user-123').delete()

Query Operations

# Simple query with single filteractive_users=scs.database.collection('users') \
.where('status', '==', 'active') \
.get()
# Multiple filtersresults=scs.database.collection('users') \
.where('age', '>=', 18) \
.where('status', '==', 'active') \
.get()
# Ordering and paginationposts=scs.database.collection('posts') \
.where('published', '==', True) \
.order_by('created_at', 'desc') \
.limit(10) \
.skip(20) \
.get()
# Using 'in' operatorfeatured=scs.database.collection('posts') \
.where('category', 'in', ['tech', 'science', 'news']) \
.get()
# Using 'contains' for array fieldstagged=scs.database.collection('posts') \
.where('tags', 'contains', 'python') \
.get()

Query Operators

OperatorDescriptionExample
==Equal to.where('status', '==', 'active')
!=Not equal to.where('status', '!=', 'deleted')
>Greater than.where('age', '>', 18)
>=Greater than or equal.where('age', '>=', 18)
<Less than.where('price', '<', 100)
<=Less than or equal.where('price', '<=', 50)
inValue in array.where('status', 'in', ['active', 'pending'])
containsArray contains value.where('tags', 'contains', 'featured')

Subcollections

# Access a subcollectionposts_ref=scs.database \
.collection('users') \
.doc('user_id') \
.collection('posts')
# Add to subcollectionpost=posts_ref.add({
'title': 'My First Post',
'content': 'Hello World!',
'created_at': datetime.now().isoformat()
})
# Query subcollectionuser_posts=posts_ref \
.order_by('created_at', 'desc') \
.limit(5) \
.get()
# Nested subcollections (e.g., users/user_id/posts/post_id/comments)comments_ref=scs.database \
.collection('users') \
.doc('user_id') \
.collection('posts') \
.doc('post_id') \
.collection('comments')

Storage

# Upload from file pathfile_info=scs.storage.upload('./image.png')
# Upload with folderfile_info=scs.storage.upload('./doc.pdf', folder='documents')
# Upload from bytesfile_info=scs.storage.upload_bytes(
data=image_bytes,
filename='image.png',
content_type='image/png'
)
# List filesfiles=scs.storage.list_files(folder='images')
# Get file referencefile_ref=scs.storage.ref('file-id')
# Download filecontent=file_ref.download()
# Download to filefile_ref.download_to_file('./local_file.png')
# Get metadatametadata=file_ref.get_metadata()
# Delete filefile_ref.delete()
# Folder operationsscs.storage.create_folder('my-folder')
scs.storage.delete_folder('my-folder')

Realtime Database

Real-time data synchronization via WebSocket:

# Connect to realtime servicescs.realtime.connect()
# Get referencechat_ref=scs.realtime.ref('chat/room1')
# Subscribe to updatesdefon_message(data, event):
print(f'Event: {event}, Data: {data}')
unsubscribe=chat_ref.on(on_message)
# Subscribe to single updatechat_ref.once(lambdadata, event: print(data))
# Unsubscribeunsubscribe()
# Disconnectscs.realtime.disconnect()

Cloud Messaging

Push notifications with topics and direct messaging:

# Register device tokenscs.messaging.register_token(
token='device-fcm-token',
platform='android'
)
# Topic operationsscs.messaging.create_topic('news', description='News updates')
scs.messaging.subscribe_to_topic('news', 'device-token')
scs.messaging.unsubscribe_from_topic('news', 'device-token')
# Send to topicscs.messaging.send_to_topic(
topic='news',
title='Breaking News',
body='Something happened!',
data={'articleId': '123'}
)
# Send to specific devicescs.messaging.send_to_token(
token='device-token',
title='Personal Alert',
body='You have a new message'
)
# Send to multiple devicesscs.messaging.send_to_tokens(
tokens=['token1', 'token2'],
title='Broadcast',
body='Hello everyone!'
)

Remote Config

Dynamic app configuration:

# Fetch configurationconfig=scs.remote_config.fetch()
# Get values with type safetytheme=scs.remote_config.get_string('app_theme', default='light')
max_items=scs.remote_config.get_int('max_items', default=10)
enabled=scs.remote_config.get_bool('feature_enabled', default=False)
settings=scs.remote_config.get_json('settings', default={})
# Admin: Manage parametersscs.remote_config.create_param(
key='app_theme',
value='dark',
description='Default app theme'
)
scs.remote_config.update_param('app_theme', 'light')
scs.remote_config.delete_param('old_param')
# Publish changesscs.remote_config.publish()
# Version managementversions=scs.remote_config.list_versions()
scs.remote_config.rollback('version-id')

Serverless Functions

Execute custom code on the backend:

# Invoke a functionresult=scs.functions.invoke('processOrder', {
'orderId': '12345',
'action': 'confirm'
})
# Admin: Create a functionscs.functions.create(
name='processOrder',
code=''' module.exports = async (data, context) => { const { orderId, action } = data; // Process order... return { success: true, orderId }; }; ''',
runtime='nodejs18',
timeout=30000,
memory=256
)
# Admin: List functionsfunctions=scs.functions.list()
# Admin: Update functionscs.functions.update('function-id', code='...')
# Admin: Test functionresult=scs.functions.test('function-id', {'test': 'data'})
# Admin: Get logslogs=scs.functions.get_logs('function-id')

AI

Chat, text completion, and image generation with local LLM models:

# Chat with AIresponse=scs.ai.chat(
message="What is the capital of France?",
system_prompt="You are a helpful geography assistant."
)
print(response['content'])
# Text completionresponse=scs.ai.complete(prompt="Once upon a time")
print(response['content'])
# Generate imageresponse=scs.ai.generate_image(prompt="A sunset over mountains")
print(response['imageUrl'])
# List available modelsmodels=scs.ai.list_models()
# Conversation managementconversation=scs.ai.create_conversation(title="Geography Chat")
scs.ai.get_conversation(conversation['conversationId'])
scs.ai.delete_conversation(conversation['conversationId'])

AI Agents

Create and manage AI agents with custom instructions and tools:

# Create an agentagent=scs.ai.create_agent(
name="Customer Support",
instructions="You are a helpful customer support assistant. Be polite and helpful.",
model="llama3.2",
temperature=0.7
)
print(f"Created agent: {agent['agentId']}")
# List agentsagents=scs.ai.list_agents()
# Run the agentresponse=scs.ai.run_agent(
agent_id=agent['agentId'],
input="How do I reset my password?"
)
print(f"Agent: {response['output']}")
print(f"Session: {response['sessionId']}")
# Continue the conversation in the same sessionresponse=scs.ai.run_agent(
agent_id=agent['agentId'],
input="Thanks! What about enabling 2FA?",
session_id=response['sessionId']
)
# List agent sessionssessions=scs.ai.list_agent_sessions(agent['agentId'])
# Get full session historysession=scs.ai.get_agent_session(agent['agentId'], response['sessionId'])
formsginsession['messages']:
print(f"{msg['role']}: {msg['content']}")
# Update agentscs.ai.update_agent(
agent_id=agent['agentId'],
instructions="Updated instructions here",
temperature=0.5
)
# Define a tool for agentstool=scs.ai.define_tool(
name="get_weather",
description="Get weather for a location",
parameters={
"type": "object",
"properties": {
"location": {"type": "string", "description": "City name"}
}
}
)
# List toolstools=scs.ai.list_tools()
# Delete agent and sessionsscs.ai.delete_agent_session(agent['agentId'], response['sessionId'])
scs.ai.delete_agent(agent['agentId'])

Configuration File

Create an scs-info.json file:

{
"sdk_config": {
"api_key": "pk_your_api_key",
"project_id": "your-project-id",
"base_url": "https://your-scs-instance.com"
}
}

Error Handling

fromscsimportSCS, SCSError, AuthenticationError, NotFoundError, ValidationErrortry:
user=scs.auth.login(email='user@example.com', password='wrong')
exceptAuthenticationErrorase:
print(f'Auth failed: {e.message}')
print(f'Status: {e.status}')
exceptNotFoundErrorase:
print(f'Not found: {e.message}')
exceptValidationErrorase:
print(f'Validation error: {e.message}')
exceptSCSErrorase:
print(f'SCS error: {e.message}')

Context Manager

withSCS({'api_key': '...', 'project_id': '...'}) asscs:
users=scs.database.collection('users').get()
# Connection automatically closed

Requirements

  • Python 3.8+
  • requests
  • python-socketio[client] (optional, for realtime functionality)

License

MIT License

About

The official Python SDK for Spyxpo Cloud Services

Resources

Code of conduct

Contributing

Security policy

Stars

2 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Latest commit

History

13 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

SCS Python SDK

Python SDK for SCS (Spyxpo Cloud Services)

Installation

pip install scs-sdk

For realtime functionality (WebSocket support):

pip install scs-sdk[realtime]

Quick Start

fromscsimportSCS# Initialize from config filescs=SCS.initialize_app('./scs-info.json')
# Or initialize with config dictscs=SCS({
'api_key': 'your-api-key',
'project_id': 'your-project-id',
'base_url': 'https://your-scs-instance.com'
})
# Authenticationuser=scs.auth.login(email='user@example.com', password='password123')
# Databaseusers=scs.database.collection('users').where('age', '>=', 18).limit(10).get()
# Storagescs.storage.upload('./image.png', folder='images')

Services

Authentication

# Register a new useruser=scs.auth.register(
email='user@example.com',
password='password123',
display_name='John Doe'
)
# Loginuser=scs.auth.login(email='user@example.com', password='password123')
# Get current userme=scs.auth.get_current_user()
# Update profilescs.auth.update_profile(display_name='Jane Doe')
# Change passwordscs.auth.change_password(
current_password='old123',
new_password='new456'
)
# Logoutscs.auth.logout()

OAuth and Social Sign-In

SCS supports multiple OAuth providers for seamless social authentication. Each provider follows a similar pattern but requires different credentials obtained from their respective SDKs.

Google Sign-In

Authenticate users with their Google account. Requires the Google Sign-In SDK on the client.

# Sign in with Googleuser=scs.auth.sign_in_with_google(
id_token='google-id-token', # Required: ID token from Google Sign-Inaccess_token='google-access-token'# Optional: Access token for additional scopes
)
print(f"User ID: {user['uid']}")
print(f"Email: {user['email']}")
print(f"Display Name: {user['displayName']}")
print(f"Photo URL: {user['photoURL']}")
print(f"Provider: {user['providerId']}") # 'google'

Parameters:

ParameterTypeRequiredDescription
id_tokenstrYesThe ID token obtained from Google Sign-In SDK
access_tokenstrNoAccess token for additional Google API scopes

Returns:dict with user data and token

Example with Google OAuth Library:

fromgoogle.oauth2importid_tokenfromgoogle.auth.transportimportrequests# Verify and get token on server sidedefverify_google_token(token):
try:
idinfo=id_token.verify_oauth2_token(
token, requests.Request(), GOOGLE_CLIENT_ID
)
returnidinfoexceptValueError:
returnNone# Sign in with the tokenuser=scs.auth.sign_in_with_google(id_token=google_token)
Facebook Sign-In

Authenticate users with their Facebook account.

# Sign in with Facebookuser=scs.auth.sign_in_with_facebook(
access_token='facebook-access-token'# Required: Access token from Facebook Login
)
print(f"User: {user['displayName']}")
print(f"Email: {user['email']}") # May be None if user didn't grant email permission

Parameters:

ParameterTypeRequiredDescription
access_tokenstrYesAccess token from Facebook Login SDK

Example with Facebook SDK:

# After getting access token from Facebook SDK on clientdeffacebook_login(access_token):
try:
user=scs.auth.sign_in_with_facebook(access_token=access_token)
returnuserexceptExceptionase:
print(f"Facebook login failed: {e}")
returnNone
Apple Sign-In

Authenticate users with their Apple ID. Ideal for iOS apps and required for apps with social login on the App Store.

# Sign in with Appleuser=scs.auth.sign_in_with_apple(
identity_token='apple-identity-token', # Required: Identity token from Sign in with Appleauthorization_code='apple-auth-code', # Optional: Authorization code for server verificationfull_name='John Doe'# Optional: User's name (only available on first sign-in)
)

Parameters:

ParameterTypeRequiredDescription
identity_tokenstrYesJWT identity token from Sign in with Apple
authorization_codestrNoAuthorization code for additional verification
full_namestrNoUser's full name (Apple only provides this on first sign-in)

Important Notes:

  • Apple only provides the user's name on the first sign-in. Store it immediately.
  • Users can choose to hide their email (Apple provides a relay email).
  • Required for apps using social login on iOS/macOS.
GitHub Sign-In

Authenticate users with their GitHub account. Popular for developer-focused applications.

# Sign in with GitHubuser=scs.auth.sign_in_with_github(
code='github-oauth-code', # Required: OAuth authorization coderedirect_uri='https://yourapp.com/callback'# Optional: Must match OAuth app settings
)
print(f"GitHub username: {user['displayName']}")
print(f"Email: {user['email']}")

Parameters:

ParameterTypeRequiredDescription
codestrYesOAuth authorization code from GitHub OAuth flow
redirect_uristrNoRedirect URI (must match your GitHub OAuth App settings)

OAuth Flow Example (Flask):

fromflaskimportFlask, redirect, requestimportrequestsapp=Flask(__name__)
GITHUB_CLIENT_ID='your-client-id'GITHUB_CLIENT_SECRET='your-client-secret'@app.route('/login/github')defgithub_login():
returnredirect(
f'https://github.com/login/oauth/authorize'f'?client_id={GITHUB_CLIENT_ID}'f'&redirect_uri=https://yourapp.com/callback'f'&scope=read:user user:email'
)
@app.route('/callback')defgithub_callback():
code=request.args.get('code')
ifcode:
user=scs.auth.sign_in_with_github(
code=code,
redirect_uri='https://yourapp.com/callback'
)
returnf"Welcome, {user['displayName']}!"return"Login failed"
Twitter/X Sign-In

Authenticate users with their Twitter/X account using OAuth 1.0a.

# Sign in with Twitter/Xuser=scs.auth.sign_in_with_twitter(
oauth_token='twitter-oauth-token', # Required: OAuth tokenoauth_token_secret='twitter-oauth-secret'# Required: OAuth token secret
)

Parameters:

ParameterTypeRequiredDescription
oauth_tokenstrYesOAuth token from Twitter authentication
oauth_token_secretstrYesOAuth token secret from Twitter authentication

Note: Twitter uses OAuth 1.0a which requires a more complex flow. Consider using a library like tweepy or python-twitter.

Microsoft Sign-In

Authenticate users with their Microsoft account (personal, work, or school accounts).

# Sign in with Microsoftuser=scs.auth.sign_in_with_microsoft(
access_token='microsoft-access-token', # Required: Access token from MSALid_token='microsoft-id-token'# Optional: ID token for additional claims
)

Parameters:

ParameterTypeRequiredDescription
access_tokenstrYesAccess token from Microsoft Authentication Library (MSAL)
id_tokenstrNoID token for additional user claims

Example with MSAL Python:

frommsalimportConfidentialClientApplicationapp=ConfidentialClientApplication(
client_id="your-client-id",
authority="https://login.microsoftonline.com/common",
client_credential="your-client-secret"
)
# After getting tokens from authorization code flowresult=app.acquire_token_by_authorization_code(
code,
scopes=["User.Read"],
redirect_uri="https://yourapp.com/callback"
)
if"access_token"inresult:
user=scs.auth.sign_in_with_microsoft(
access_token=result["access_token"],
id_token=result.get("id_token")
)

Anonymous Authentication

Allow users to use your app without creating an account. Anonymous accounts can later be upgraded to permanent accounts by linking a provider.

# Sign in anonymously (creates a temporary account)user=scs.auth.sign_in_anonymously(
custom_data={'referrer': 'landing-page', 'campaign': 'summer-sale'} # optional
)
print(f"Anonymous user ID: {user['uid']}")
print(f"Is anonymous: {user['isAnonymous']}") # True

Parameters:

ParameterTypeRequiredDescription
custom_datadictNoCustom data to store with the anonymous user for analytics

Use Cases:

  • Allow users to try your app before signing up
  • Guest checkout in e-commerce
  • Save user progress/preferences before account creation
  • A/B testing with user tracking

Converting Anonymous to Permanent Account:

# User decides to create a permanent account# Link their anonymous account to a providertry:
user=scs.auth.link_provider('google', {
'idToken': 'google-id-token'
})
print("Account upgraded! User data preserved.")
print(f"Is anonymous: {user['isAnonymous']}") # FalseexceptExceptionase:
if'credential-already-in-use'instr(e):
# This Google account is already linked to another userprint("This account is already registered. Please sign in instead.")
else:
raise

Phone Number Authentication

Two-step authentication flow using SMS verification codes.

# Step 1: Send verification code to phoneresult=scs.auth.send_phone_verification_code(
phone_number='+1234567890', # Required: E.164 formatrecaptcha_token='recaptcha-token'# Optional: For bot protection
)
verification_id=result['verificationId']
print(f"Verification ID: {verification_id}")
# Store this ID - you'll need it in step 2# Step 2: User enters the code they receiveduser=scs.auth.sign_in_with_phone_number(
verification_id=verification_id, # The ID from step 1code='123456'# 6-digit code from SMS
)
print(f"Phone verified: {user['phoneNumber']}")

send_phone_verification_code Parameters:

ParameterTypeRequiredDescription
phone_numberstrYesPhone number in E.164 format (e.g., +1234567890)
recaptcha_tokenstrNoreCAPTCHA token for abuse prevention

sign_in_with_phone_number Parameters:

ParameterTypeRequiredDescription
verification_idstrYesVerification ID from send_phone_verification_code
codestrYes6-digit verification code from SMS

Complete Flow Example:

defsign_in_with_phone(phone_number: str, get_code_callback) ->dict:
""" Sign in with phone number. Args: phone_number: Phone number in E.164 format get_code_callback: Function that prompts user for the SMS code Returns: User dict on success """try:
# Step 1: Send coderesult=scs.auth.send_phone_verification_code(
phone_number=phone_number
)
verification_id=result['verificationId']
# Get code from user (your UI implementation)code=get_code_callback()
# Step 2: Verify codeuser=scs.auth.sign_in_with_phone_number(
verification_id=verification_id,
code=code
)
returnuserexceptExceptionase:
error_msg=str(e).lower()
if'invalid-phone-number'inerror_msg:
raiseValueError('Invalid phone number format. Use E.164 format (+1234567890)')
elif'too-many-requests'inerror_msg:
raiseValueError('Too many attempts. Please try again later.')
elif'invalid-verification-code'inerror_msg:
raiseValueError('Invalid verification code. Please try again.')
elif'code-expired'inerror_msg:
raiseValueError('Code expired. Please request a new one.')
else:
raise

Custom Token Authentication

Sign in using a JWT token generated by your own backend. Useful for migrating users from another system or integrating with custom authentication.

# Sign in with a custom token (generated by your backend)user=scs.auth.sign_in_with_custom_token('your-custom-jwt-token')
print(f"Signed in user: {user['uid']}")

Parameters:

ParameterTypeRequiredDescription
tokenstrYesJWT token generated by your backend

Backend Token Generation Example:

importjwtimporttimeimportosdefcreate_custom_token(uid: str, claims: dict=None) ->str:
""" Create a custom authentication token. Args: uid: Unique user identifier claims: Optional custom claims to include Returns: JWT token string """payload= {
'uid': uid,
'claims': claimsor {},
'iat': int(time.time()),
'exp': int(time.time()) +3600# 1 hour
}
returnjwt.encode(
payload,
os.environ['SCS_SECRET_KEY'],
algorithm='HS256'
)
# Generate token for a usercustom_token=create_custom_token('user-123', {'role': 'admin'})
# Send this token to the client for sign-in

Use Cases:

  • Migrating users from another authentication system
  • Server-side user creation with immediate client sign-in
  • Integration with enterprise SSO systems (SAML, LDAP)
  • Machine-to-machine authentication

Account Linking

Link multiple authentication providers to a single account. Users can sign in with any linked provider.

# Link a provider to current accountuser=scs.auth.link_provider('facebook', {
'accessToken': 'facebook-access-token'
})
print(f"Linked providers: {user['providerData']}")
# [{'providerId': 'password'}, {'providerId': 'google'}, {'providerId': 'facebook'}]# Unlink a provider from current accountuser=scs.auth.unlink_provider('facebook')
print(f"Remaining providers: {user['providerData']}")
# Get available sign-in methods for an emailresult=scs.auth.fetch_sign_in_methods_for_email('user@example.com')
print(f"Available methods: {result['methods']}")
# ['password', 'google', 'facebook']

link_provider Parameters:

ParameterTypeRequiredDescription
providerstrYesProvider name: 'google', 'facebook', 'apple', 'github', 'twitter', 'microsoft'
credentialsdictYesProvider-specific credentials (tokens)

Supported Providers and Credentials:

ProviderRequired Credentials
google{'idToken': '...', 'accessToken': '...'} (accessToken optional)
facebook{'accessToken': '...'}
apple{'identityToken': '...', 'authorizationCode': '...', 'fullName': '...'}
github{'code': '...', 'redirectUri': '...'}
twitter{'oauthToken': '...', 'oauthTokenSecret': '...'}
microsoft{'accessToken': '...', 'idToken': '...'}

Complete Account Linking Flow:

defcan_link_provider(email: str, provider: str) ->bool:
"""Check if a provider can be linked to an account."""result=scs.auth.fetch_sign_in_methods_for_email(email)
methods=result.get('methods', [])
ifproviderinmethods:
raiseValueError(f"{provider} is already linked to this account")
returnTruedeflink_google_account(google_id_token: str) ->dict:
"""Link Google to the current user's account."""try:
# Verify user is signed incurrent_user=scs.auth.get_current_user()
ifnotcurrent_user:
raiseValueError('Must be signed in to link accounts')
# Link the provideruser=scs.auth.link_provider('google', {
'idToken': google_id_token
})
print('Successfully linked Google account')
returnuserexceptExceptionase:
error_msg=str(e).lower()
if'credential-already-in-use'inerror_msg:
print('This Google account is already linked to another user')
elif'provider-already-linked'inerror_msg:
print('Google is already linked to this account')
raise

Password Reset & Email Verification

Handle password recovery and email verification flows.

# Send password reset emailscs.auth.send_password_reset_email('user@example.com')
print("Password reset email sent")
# Confirm password reset (user clicks link in email, you extract the code)scs.auth.confirm_password_reset(
code='reset-code-from-email', # Code from the reset linknew_password='newSecurePassword123'# User's new password
)
print("Password successfully reset")
# Send email verification to current userscs.auth.send_email_verification()
print("Verification email sent")
# Verify email with code (user clicks link, you extract the code)scs.auth.verify_email('verification-code')
print("Email verified")

Error Handling:

defreset_password(email: str) ->dict:
"""Send password reset email with error handling."""try:
scs.auth.send_password_reset_email(email)
return {'success': True, 'message': 'Reset email sent'}
exceptExceptionase:
error_msg=str(e).lower()
if'user-not-found'inerror_msg:
# Don't reveal if user exists for securityreturn {'success': True, 'message': 'If this email exists, a reset link was sent'}
elif'too-many-requests'inerror_msg:
return {'success': False, 'message': 'Too many attempts. Please try later.'}
else:
raisedefconfirm_reset(code: str, new_password: str) ->dict:
"""Confirm password reset with validation."""# Validate password strengthiflen(new_password) <8:
raiseValueError('Password must be at least 8 characters')
try:
scs.auth.confirm_password_reset(code=code, new_password=new_password)
return {'success': True}
exceptExceptionase:
error_msg=str(e).lower()
if'expired-action-code'inerror_msg:
return {'success': False, 'message': 'Reset link expired. Please request a new one.'}
elif'invalid-action-code'inerror_msg:
return {'success': False, 'message': 'Invalid reset link.'}
elif'weak-password'inerror_msg:
return {'success': False, 'message': 'Password is too weak.'}
else:
raise

Authentication State Management

Manage user sessions and authentication state.

# Check if user is logged inis_logged_in=scs.auth.is_logged_inprint(f"Is logged in: {is_logged_in}")
# Get current user (from cache)cached_user=scs.auth.current_user# Get current user (fresh from server)user=scs.auth.get_current_user()
# Refresh user datarefreshed_user=scs.auth.reload()
# Get the current auth tokentoken=scs.auth.token

Complete Authentication Example

fromscsimportSCS, AuthenticationErrorclassAuthService:
def__init__(self):
self.scs=SCS({
'api_key': 'your-api-key',
'project_id': 'your-project-id',
'base_url': 'https://your-scs-instance.com'
})
defregister(self, email: str, password: str, display_name: str) ->dict:
"""Register a new user with email verification."""user=self.scs.auth.register(
email=email,
password=password,
display_name=display_name
)
# Send verification emailself.scs.auth.send_email_verification()
returnuserdeflogin(self, email: str, password: str) ->dict:
"""Login with email and password."""returnself.scs.auth.login(email=email, password=password)
defsocial_login(self, provider: str, credentials: dict) ->dict:
"""Login with any social provider."""methods= {
'google': lambda: self.scs.auth.sign_in_with_google(**credentials),
'facebook': lambda: self.scs.auth.sign_in_with_facebook(**credentials),
'apple': lambda: self.scs.auth.sign_in_with_apple(**credentials),
'github': lambda: self.scs.auth.sign_in_with_github(**credentials),
'twitter': lambda: self.scs.auth.sign_in_with_twitter(**credentials),
'microsoft': lambda: self.scs.auth.sign_in_with_microsoft(**credentials)
}
ifprovidernotinmethods:
raiseValueError(f"Unknown provider: {provider}")
returnmethods[provider]()
defcontinue_as_guest(self, custom_data: dict=None) ->dict:
"""Sign in anonymously for guest access."""returnself.scs.auth.sign_in_anonymously(custom_data=custom_dataor {})
defupgrade_guest_account(self, provider: str, credentials: dict) ->dict:
"""Upgrade anonymous account to permanent."""user=self.scs.auth.get_current_user()
ifnotuserornotuser.get('isAnonymous'):
raiseValueError('Current user is not anonymous')
returnself.scs.auth.link_provider(provider, credentials)
deflogout(self):
"""Sign out the current user."""self.scs.auth.logout()
# Usageauth=AuthService()
# Register new useruser=auth.register('user@example.com', 'password123', 'John Doe')
# Or sign in with Googlegoogle_user=auth.social_login('google', {'id_token': 'xxx'})
# Or continue as guest and upgrade laterguest=auth.continue_as_guest({'source': 'homepage'})
# ... user decides to create account ...upgraded=auth.upgrade_guest_account('google', {'idToken': 'xxx'})

Database

Document database with query builder. SCS supports two powerful database options:

Database Types

TypeNameDescriptionBest For
eazieaZI DatabaseDocument-based NoSQL with Firestore-like collections, documents, and subcollectionsDevelopment, prototyping, small to medium apps
reladbRelaDBProduction-grade NoSQL database with relational-style viewsProduction, scalability, advanced queries

Initialize with eaZI (Default)

fromscsimportSCS# eaZI is the default database - no special configuration neededscs=SCS({
'project_id': 'your-project-id',
'api_key': 'your-api-key'# database_type: 'eazi' is implicit
})

Initialize with RelaDB (Production)

fromscsimportSCS# Use RelaDB for productionscs=SCS({
'project_id': 'your-project-id',
'api_key': 'your-api-key',
'database_type': 'reladb'# Enable RelaDB
})

eaZI Database Features

  • Document-based: Firestore-like collections and documents
  • Subcollections: Nested data organization
  • File-based storage: No external dependencies required
  • Zero configuration: Works out of the box
  • Query support: Filtering, ordering, and pagination

RelaDB Features

  • Production-ready: Built for reliability and performance
  • Scalable: Horizontal scaling and replication support
  • Advanced queries: Aggregation pipelines, complex filters
  • Indexing: Custom indexes for optimized performance
  • Schema flexibility: Dynamic schema with validation support
  • Relational-style views: Table view with columns and rows in the console

Collection Operations

# Get a collection referenceusers=scs.database.collection('users')
# List all collectionscollections=scs.database.list_collections()
# Create a collectionscs.database.create_collection('new_collection')
# Delete a collectionscs.database.delete_collection('old_collection')

Document Operations

# Add document with auto-generated IDdoc=scs.database.collection('users').add({
'name': 'John Doe',
'email': 'john@example.com',
'age': 30,
'tags': ['developer', 'python'],
'profile': {
'bio': 'Software developer',
'avatar': 'https://example.com/avatar.jpg'
}
})
print(f'Document ID: {doc["id"]}')
# Set document with custom ID (creates or overwrites)scs.database.collection('users').doc('user-123').set({
'name': 'Jane Doe',
'email': 'jane@example.com'
})
# Get a single documentuser=scs.database.collection('users').doc('user-123').get()
print(user)
# Update document (partial update)scs.database.collection('users').doc('user-123').update({
'age': 31,
'profile.bio': 'Senior developer'
})
# Delete documentscs.database.collection('users').doc('user-123').delete()

Query Operations

# Simple query with single filteractive_users=scs.database.collection('users') \
.where('status', '==', 'active') \
.get()
# Multiple filtersresults=scs.database.collection('users') \
.where('age', '>=', 18) \
.where('status', '==', 'active') \
.get()
# Ordering and paginationposts=scs.database.collection('posts') \
.where('published', '==', True) \
.order_by('created_at', 'desc') \
.limit(10) \
.skip(20) \
.get()
# Using 'in' operatorfeatured=scs.database.collection('posts') \
.where('category', 'in', ['tech', 'science', 'news']) \
.get()
# Using 'contains' for array fieldstagged=scs.database.collection('posts') \
.where('tags', 'contains', 'python') \
.get()

Query Operators

OperatorDescriptionExample
==Equal to.where('status', '==', 'active')
!=Not equal to.where('status', '!=', 'deleted')
>Greater than.where('age', '>', 18)
>=Greater than or equal.where('age', '>=', 18)
<Less than.where('price', '<', 100)
<=Less than or equal.where('price', '<=', 50)
inValue in array.where('status', 'in', ['active', 'pending'])
containsArray contains value.where('tags', 'contains', 'featured')

Subcollections

# Access a subcollectionposts_ref=scs.database \
.collection('users') \
.doc('user_id') \
.collection('posts')
# Add to subcollectionpost=posts_ref.add({
'title': 'My First Post',
'content': 'Hello World!',
'created_at': datetime.now().isoformat()
})
# Query subcollectionuser_posts=posts_ref \
.order_by('created_at', 'desc') \
.limit(5) \
.get()
# Nested subcollections (e.g., users/user_id/posts/post_id/comments)comments_ref=scs.database \
.collection('users') \
.doc('user_id') \
.collection('posts') \
.doc('post_id') \
.collection('comments')

Storage

# Upload from file pathfile_info=scs.storage.upload('./image.png')
# Upload with folderfile_info=scs.storage.upload('./doc.pdf', folder='documents')
# Upload from bytesfile_info=scs.storage.upload_bytes(
data=image_bytes,
filename='image.png',
content_type='image/png'
)
# List filesfiles=scs.storage.list_files(folder='images')
# Get file referencefile_ref=scs.storage.ref('file-id')
# Download filecontent=file_ref.download()
# Download to filefile_ref.download_to_file('./local_file.png')
# Get metadatametadata=file_ref.get_metadata()
# Delete filefile_ref.delete()
# Folder operationsscs.storage.create_folder('my-folder')
scs.storage.delete_folder('my-folder')

Realtime Database

Real-time data synchronization via WebSocket:

# Connect to realtime servicescs.realtime.connect()
# Get referencechat_ref=scs.realtime.ref('chat/room1')
# Subscribe to updatesdefon_message(data, event):
print(f'Event: {event}, Data: {data}')
unsubscribe=chat_ref.on(on_message)
# Subscribe to single updatechat_ref.once(lambdadata, event: print(data))
# Unsubscribeunsubscribe()
# Disconnectscs.realtime.disconnect()

Cloud Messaging

Push notifications with topics and direct messaging:

# Register device tokenscs.messaging.register_token(
token='device-fcm-token',
platform='android'
)
# Topic operationsscs.messaging.create_topic('news', description='News updates')
scs.messaging.subscribe_to_topic('news', 'device-token')
scs.messaging.unsubscribe_from_topic('news', 'device-token')
# Send to topicscs.messaging.send_to_topic(
topic='news',
title='Breaking News',
body='Something happened!',
data={'articleId': '123'}
)
# Send to specific devicescs.messaging.send_to_token(
token='device-token',
title='Personal Alert',
body='You have a new message'
)
# Send to multiple devicesscs.messaging.send_to_tokens(
tokens=['token1', 'token2'],
title='Broadcast',
body='Hello everyone!'
)

Remote Config

Dynamic app configuration:

# Fetch configurationconfig=scs.remote_config.fetch()
# Get values with type safetytheme=scs.remote_config.get_string('app_theme', default='light')
max_items=scs.remote_config.get_int('max_items', default=10)
enabled=scs.remote_config.get_bool('feature_enabled', default=False)
settings=scs.remote_config.get_json('settings', default={})
# Admin: Manage parametersscs.remote_config.create_param(
key='app_theme',
value='dark',
description='Default app theme'
)
scs.remote_config.update_param('app_theme', 'light')
scs.remote_config.delete_param('old_param')
# Publish changesscs.remote_config.publish()
# Version managementversions=scs.remote_config.list_versions()
scs.remote_config.rollback('version-id')

Serverless Functions

Execute custom code on the backend:

# Invoke a functionresult=scs.functions.invoke('processOrder', {
'orderId': '12345',
'action': 'confirm'
})
# Admin: Create a functionscs.functions.create(
name='processOrder',
code=''' module.exports = async (data, context) => { const { orderId, action } = data; // Process order... return { success: true, orderId }; }; ''',
runtime='nodejs18',
timeout=30000,
memory=256
)
# Admin: List functionsfunctions=scs.functions.list()
# Admin: Update functionscs.functions.update('function-id', code='...')
# Admin: Test functionresult=scs.functions.test('function-id', {'test': 'data'})
# Admin: Get logslogs=scs.functions.get_logs('function-id')

AI

Chat, text completion, and image generation with local LLM models:

# Chat with AIresponse=scs.ai.chat(
message="What is the capital of France?",
system_prompt="You are a helpful geography assistant."
)
print(response['content'])
# Text completionresponse=scs.ai.complete(prompt="Once upon a time")
print(response['content'])
# Generate imageresponse=scs.ai.generate_image(prompt="A sunset over mountains")
print(response['imageUrl'])
# List available modelsmodels=scs.ai.list_models()
# Conversation managementconversation=scs.ai.create_conversation(title="Geography Chat")
scs.ai.get_conversation(conversation['conversationId'])
scs.ai.delete_conversation(conversation['conversationId'])

AI Agents

Create and manage AI agents with custom instructions and tools:

# Create an agentagent=scs.ai.create_agent(
name="Customer Support",
instructions="You are a helpful customer support assistant. Be polite and helpful.",
model="llama3.2",
temperature=0.7
)
print(f"Created agent: {agent['agentId']}")
# List agentsagents=scs.ai.list_agents()
# Run the agentresponse=scs.ai.run_agent(
agent_id=agent['agentId'],
input="How do I reset my password?"
)
print(f"Agent: {response['output']}")
print(f"Session: {response['sessionId']}")
# Continue the conversation in the same sessionresponse=scs.ai.run_agent(
agent_id=agent['agentId'],
input="Thanks! What about enabling 2FA?",
session_id=response['sessionId']
)
# List agent sessionssessions=scs.ai.list_agent_sessions(agent['agentId'])
# Get full session historysession=scs.ai.get_agent_session(agent['agentId'], response['sessionId'])
formsginsession['messages']:
print(f"{msg['role']}: {msg['content']}")
# Update agentscs.ai.update_agent(
agent_id=agent['agentId'],
instructions="Updated instructions here",
temperature=0.5
)
# Define a tool for agentstool=scs.ai.define_tool(
name="get_weather",
description="Get weather for a location",
parameters={
"type": "object",
"properties": {
"location": {"type": "string", "description": "City name"}
}
}
)
# List toolstools=scs.ai.list_tools()
# Delete agent and sessionsscs.ai.delete_agent_session(agent['agentId'], response['sessionId'])
scs.ai.delete_agent(agent['agentId'])

Configuration File

Create an scs-info.json file:

{
"sdk_config": {
"api_key": "pk_your_api_key",
"project_id": "your-project-id",
"base_url": "https://your-scs-instance.com"
}
}

Error Handling

fromscsimportSCS, SCSError, AuthenticationError, NotFoundError, ValidationErrortry:
user=scs.auth.login(email='user@example.com', password='wrong')
exceptAuthenticationErrorase:
print(f'Auth failed: {e.message}')
print(f'Status: {e.status}')
exceptNotFoundErrorase:
print(f'Not found: {e.message}')
exceptValidationErrorase:
print(f'Validation error: {e.message}')
exceptSCSErrorase:
print(f'SCS error: {e.message}')

Context Manager

withSCS({'api_key': '...', 'project_id': '...'}) asscs:
users=scs.database.collection('users').get()
# Connection automatically closed

Requirements

  • Python 3.8+
  • requests
  • python-socketio[client] (optional, for realtime functionality)

License

MIT License

About

The official Python SDK for Spyxpo Cloud Services

Resources

Code of conduct

Contributing

Security policy

Stars

2 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Latest commit

History

13 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

SCS Python SDK

Python SDK for SCS (Spyxpo Cloud Services)

Installation

pip install scs-sdk

For realtime functionality (WebSocket support):

pip install scs-sdk[realtime]

Quick Start

fromscsimportSCS# Initialize from config filescs=SCS.initialize_app('./scs-info.json')
# Or initialize with config dictscs=SCS({
'api_key': 'your-api-key',
'project_id': 'your-project-id',
'base_url': 'https://your-scs-instance.com'
})
# Authenticationuser=scs.auth.login(email='user@example.com', password='password123')
# Databaseusers=scs.database.collection('users').where('age', '>=', 18).limit(10).get()
# Storagescs.storage.upload('./image.png', folder='images')

Services

Authentication

# Register a new useruser=scs.auth.register(
email='user@example.com',
password='password123',
display_name='John Doe'
)
# Loginuser=scs.auth.login(email='user@example.com', password='password123')
# Get current userme=scs.auth.get_current_user()
# Update profilescs.auth.update_profile(display_name='Jane Doe')
# Change passwordscs.auth.change_password(
current_password='old123',
new_password='new456'
)
# Logoutscs.auth.logout()

OAuth and Social Sign-In

SCS supports multiple OAuth providers for seamless social authentication. Each provider follows a similar pattern but requires different credentials obtained from their respective SDKs.

Google Sign-In

Authenticate users with their Google account. Requires the Google Sign-In SDK on the client.

# Sign in with Googleuser=scs.auth.sign_in_with_google(
id_token='google-id-token', # Required: ID token from Google Sign-Inaccess_token='google-access-token'# Optional: Access token for additional scopes
)
print(f"User ID: {user['uid']}")
print(f"Email: {user['email']}")
print(f"Display Name: {user['displayName']}")
print(f"Photo URL: {user['photoURL']}")
print(f"Provider: {user['providerId']}") # 'google'

Parameters:

ParameterTypeRequiredDescription
id_tokenstrYesThe ID token obtained from Google Sign-In SDK
access_tokenstrNoAccess token for additional Google API scopes

Returns:dict with user data and token

Example with Google OAuth Library:

fromgoogle.oauth2importid_tokenfromgoogle.auth.transportimportrequests# Verify and get token on server sidedefverify_google_token(token):
try:
idinfo=id_token.verify_oauth2_token(
token, requests.Request(), GOOGLE_CLIENT_ID
)
returnidinfoexceptValueError:
returnNone# Sign in with the tokenuser=scs.auth.sign_in_with_google(id_token=google_token)
Facebook Sign-In

Authenticate users with their Facebook account.

# Sign in with Facebookuser=scs.auth.sign_in_with_facebook(
access_token='facebook-access-token'# Required: Access token from Facebook Login
)
print(f"User: {user['displayName']}")
print(f"Email: {user['email']}") # May be None if user didn't grant email permission

Parameters:

ParameterTypeRequiredDescription
access_tokenstrYesAccess token from Facebook Login SDK

Example with Facebook SDK:

# After getting access token from Facebook SDK on clientdeffacebook_login(access_token):
try:
user=scs.auth.sign_in_with_facebook(access_token=access_token)
returnuserexceptExceptionase:
print(f"Facebook login failed: {e}")
returnNone
Apple Sign-In

Authenticate users with their Apple ID. Ideal for iOS apps and required for apps with social login on the App Store.

# Sign in with Appleuser=scs.auth.sign_in_with_apple(
identity_token='apple-identity-token', # Required: Identity token from Sign in with Appleauthorization_code='apple-auth-code', # Optional: Authorization code for server verificationfull_name='John Doe'# Optional: User's name (only available on first sign-in)
)

Parameters:

ParameterTypeRequiredDescription
identity_tokenstrYesJWT identity token from Sign in with Apple
authorization_codestrNoAuthorization code for additional verification
full_namestrNoUser's full name (Apple only provides this on first sign-in)

Important Notes:

  • Apple only provides the user's name on the first sign-in. Store it immediately.
  • Users can choose to hide their email (Apple provides a relay email).
  • Required for apps using social login on iOS/macOS.
GitHub Sign-In

Authenticate users with their GitHub account. Popular for developer-focused applications.

# Sign in with GitHubuser=scs.auth.sign_in_with_github(
code='github-oauth-code', # Required: OAuth authorization coderedirect_uri='https://yourapp.com/callback'# Optional: Must match OAuth app settings
)
print(f"GitHub username: {user['displayName']}")
print(f"Email: {user['email']}")

Parameters:

ParameterTypeRequiredDescription
codestrYesOAuth authorization code from GitHub OAuth flow
redirect_uristrNoRedirect URI (must match your GitHub OAuth App settings)

OAuth Flow Example (Flask):

fromflaskimportFlask, redirect, requestimportrequestsapp=Flask(__name__)
GITHUB_CLIENT_ID='your-client-id'GITHUB_CLIENT_SECRET='your-client-secret'@app.route('/login/github')defgithub_login():
returnredirect(
f'https://github.com/login/oauth/authorize'f'?client_id={GITHUB_CLIENT_ID}'f'&redirect_uri=https://yourapp.com/callback'f'&scope=read:user user:email'
)
@app.route('/callback')defgithub_callback():
code=request.args.get('code')
ifcode:
user=scs.auth.sign_in_with_github(
code=code,
redirect_uri='https://yourapp.com/callback'
)
returnf"Welcome, {user['displayName']}!"return"Login failed"
Twitter/X Sign-In

Authenticate users with their Twitter/X account using OAuth 1.0a.

# Sign in with Twitter/Xuser=scs.auth.sign_in_with_twitter(
oauth_token='twitter-oauth-token', # Required: OAuth tokenoauth_token_secret='twitter-oauth-secret'# Required: OAuth token secret
)

Parameters:

ParameterTypeRequiredDescription
oauth_tokenstrYesOAuth token from Twitter authentication
oauth_token_secretstrYesOAuth token secret from Twitter authentication

Note: Twitter uses OAuth 1.0a which requires a more complex flow. Consider using a library like tweepy or python-twitter.

Microsoft Sign-In

Authenticate users with their Microsoft account (personal, work, or school accounts).

# Sign in with Microsoftuser=scs.auth.sign_in_with_microsoft(
access_token='microsoft-access-token', # Required: Access token from MSALid_token='microsoft-id-token'# Optional: ID token for additional claims
)

Parameters:

ParameterTypeRequiredDescription
access_tokenstrYesAccess token from Microsoft Authentication Library (MSAL)
id_tokenstrNoID token for additional user claims

Example with MSAL Python:

frommsalimportConfidentialClientApplicationapp=ConfidentialClientApplication(
client_id="your-client-id",
authority="https://login.microsoftonline.com/common",
client_credential="your-client-secret"
)
# After getting tokens from authorization code flowresult=app.acquire_token_by_authorization_code(
code,
scopes=["User.Read"],
redirect_uri="https://yourapp.com/callback"
)
if"access_token"inresult:
user=scs.auth.sign_in_with_microsoft(
access_token=result["access_token"],
id_token=result.get("id_token")
)

Anonymous Authentication

Allow users to use your app without creating an account. Anonymous accounts can later be upgraded to permanent accounts by linking a provider.

# Sign in anonymously (creates a temporary account)user=scs.auth.sign_in_anonymously(
custom_data={'referrer': 'landing-page', 'campaign': 'summer-sale'} # optional
)
print(f"Anonymous user ID: {user['uid']}")
print(f"Is anonymous: {user['isAnonymous']}") # True

Parameters:

ParameterTypeRequiredDescription
custom_datadictNoCustom data to store with the anonymous user for analytics

Use Cases:

  • Allow users to try your app before signing up
  • Guest checkout in e-commerce
  • Save user progress/preferences before account creation
  • A/B testing with user tracking

Converting Anonymous to Permanent Account:

# User decides to create a permanent account# Link their anonymous account to a providertry:
user=scs.auth.link_provider('google', {
'idToken': 'google-id-token'
})
print("Account upgraded! User data preserved.")
print(f"Is anonymous: {user['isAnonymous']}") # FalseexceptExceptionase:
if'credential-already-in-use'instr(e):
# This Google account is already linked to another userprint("This account is already registered. Please sign in instead.")
else:
raise

Phone Number Authentication

Two-step authentication flow using SMS verification codes.

# Step 1: Send verification code to phoneresult=scs.auth.send_phone_verification_code(
phone_number='+1234567890', # Required: E.164 formatrecaptcha_token='recaptcha-token'# Optional: For bot protection
)
verification_id=result['verificationId']
print(f"Verification ID: {verification_id}")
# Store this ID - you'll need it in step 2# Step 2: User enters the code they receiveduser=scs.auth.sign_in_with_phone_number(
verification_id=verification_id, # The ID from step 1code='123456'# 6-digit code from SMS
)
print(f"Phone verified: {user['phoneNumber']}")

send_phone_verification_code Parameters:

ParameterTypeRequiredDescription
phone_numberstrYesPhone number in E.164 format (e.g., +1234567890)
recaptcha_tokenstrNoreCAPTCHA token for abuse prevention

sign_in_with_phone_number Parameters:

ParameterTypeRequiredDescription
verification_idstrYesVerification ID from send_phone_verification_code
codestrYes6-digit verification code from SMS

Complete Flow Example:

defsign_in_with_phone(phone_number: str, get_code_callback) ->dict:
""" Sign in with phone number. Args: phone_number: Phone number in E.164 format get_code_callback: Function that prompts user for the SMS code Returns: User dict on success """try:
# Step 1: Send coderesult=scs.auth.send_phone_verification_code(
phone_number=phone_number
)
verification_id=result['verificationId']
# Get code from user (your UI implementation)code=get_code_callback()
# Step 2: Verify codeuser=scs.auth.sign_in_with_phone_number(
verification_id=verification_id,
code=code
)
returnuserexceptExceptionase:
error_msg=str(e).lower()
if'invalid-phone-number'inerror_msg:
raiseValueError('Invalid phone number format. Use E.164 format (+1234567890)')
elif'too-many-requests'inerror_msg:
raiseValueError('Too many attempts. Please try again later.')
elif'invalid-verification-code'inerror_msg:
raiseValueError('Invalid verification code. Please try again.')
elif'code-expired'inerror_msg:
raiseValueError('Code expired. Please request a new one.')
else:
raise

Custom Token Authentication

Sign in using a JWT token generated by your own backend. Useful for migrating users from another system or integrating with custom authentication.

# Sign in with a custom token (generated by your backend)user=scs.auth.sign_in_with_custom_token('your-custom-jwt-token')
print(f"Signed in user: {user['uid']}")

Parameters:

ParameterTypeRequiredDescription
tokenstrYesJWT token generated by your backend

Backend Token Generation Example:

importjwtimporttimeimportosdefcreate_custom_token(uid: str, claims: dict=None) ->str:
""" Create a custom authentication token. Args: uid: Unique user identifier claims: Optional custom claims to include Returns: JWT token string """payload= {
'uid': uid,
'claims': claimsor {},
'iat': int(time.time()),
'exp': int(time.time()) +3600# 1 hour
}
returnjwt.encode(
payload,
os.environ['SCS_SECRET_KEY'],
algorithm='HS256'
)
# Generate token for a usercustom_token=create_custom_token('user-123', {'role': 'admin'})
# Send this token to the client for sign-in

Use Cases:

  • Migrating users from another authentication system
  • Server-side user creation with immediate client sign-in
  • Integration with enterprise SSO systems (SAML, LDAP)
  • Machine-to-machine authentication

Account Linking

Link multiple authentication providers to a single account. Users can sign in with any linked provider.

# Link a provider to current accountuser=scs.auth.link_provider('facebook', {
'accessToken': 'facebook-access-token'
})
print(f"Linked providers: {user['providerData']}")
# [{'providerId': 'password'}, {'providerId': 'google'}, {'providerId': 'facebook'}]# Unlink a provider from current accountuser=scs.auth.unlink_provider('facebook')
print(f"Remaining providers: {user['providerData']}")
# Get available sign-in methods for an emailresult=scs.auth.fetch_sign_in_methods_for_email('user@example.com')
print(f"Available methods: {result['methods']}")
# ['password', 'google', 'facebook']

link_provider Parameters:

ParameterTypeRequiredDescription
providerstrYesProvider name: 'google', 'facebook', 'apple', 'github', 'twitter', 'microsoft'
credentialsdictYesProvider-specific credentials (tokens)

Supported Providers and Credentials:

ProviderRequired Credentials
google{'idToken': '...', 'accessToken': '...'} (accessToken optional)
facebook{'accessToken': '...'}
apple{'identityToken': '...', 'authorizationCode': '...', 'fullName': '...'}
github{'code': '...', 'redirectUri': '...'}
twitter{'oauthToken': '...', 'oauthTokenSecret': '...'}
microsoft{'accessToken': '...', 'idToken': '...'}

Complete Account Linking Flow:

defcan_link_provider(email: str, provider: str) ->bool:
"""Check if a provider can be linked to an account."""result=scs.auth.fetch_sign_in_methods_for_email(email)
methods=result.get('methods', [])
ifproviderinmethods:
raiseValueError(f"{provider} is already linked to this account")
returnTruedeflink_google_account(google_id_token: str) ->dict:
"""Link Google to the current user's account."""try:
# Verify user is signed incurrent_user=scs.auth.get_current_user()
ifnotcurrent_user:
raiseValueError('Must be signed in to link accounts')
# Link the provideruser=scs.auth.link_provider('google', {
'idToken': google_id_token
})
print('Successfully linked Google account')
returnuserexceptExceptionase:
error_msg=str(e).lower()
if'credential-already-in-use'inerror_msg:
print('This Google account is already linked to another user')
elif'provider-already-linked'inerror_msg:
print('Google is already linked to this account')
raise

Password Reset & Email Verification

Handle password recovery and email verification flows.

# Send password reset emailscs.auth.send_password_reset_email('user@example.com')
print("Password reset email sent")
# Confirm password reset (user clicks link in email, you extract the code)scs.auth.confirm_password_reset(
code='reset-code-from-email', # Code from the reset linknew_password='newSecurePassword123'# User's new password
)
print("Password successfully reset")
# Send email verification to current userscs.auth.send_email_verification()
print("Verification email sent")
# Verify email with code (user clicks link, you extract the code)scs.auth.verify_email('verification-code')
print("Email verified")

Error Handling:

defreset_password(email: str) ->dict:
"""Send password reset email with error handling."""try:
scs.auth.send_password_reset_email(email)
return {'success': True, 'message': 'Reset email sent'}
exceptExceptionase:
error_msg=str(e).lower()
if'user-not-found'inerror_msg:
# Don't reveal if user exists for securityreturn {'success': True, 'message': 'If this email exists, a reset link was sent'}
elif'too-many-requests'inerror_msg:
return {'success': False, 'message': 'Too many attempts. Please try later.'}
else:
raisedefconfirm_reset(code: str, new_password: str) ->dict:
"""Confirm password reset with validation."""# Validate password strengthiflen(new_password) <8:
raiseValueError('Password must be at least 8 characters')
try:
scs.auth.confirm_password_reset(code=code, new_password=new_password)
return {'success': True}
exceptExceptionase:
error_msg=str(e).lower()
if'expired-action-code'inerror_msg:
return {'success': False, 'message': 'Reset link expired. Please request a new one.'}
elif'invalid-action-code'inerror_msg:
return {'success': False, 'message': 'Invalid reset link.'}
elif'weak-password'inerror_msg:
return {'success': False, 'message': 'Password is too weak.'}
else:
raise

Authentication State Management

Manage user sessions and authentication state.

# Check if user is logged inis_logged_in=scs.auth.is_logged_inprint(f"Is logged in: {is_logged_in}")
# Get current user (from cache)cached_user=scs.auth.current_user# Get current user (fresh from server)user=scs.auth.get_current_user()
# Refresh user datarefreshed_user=scs.auth.reload()
# Get the current auth tokentoken=scs.auth.token

Complete Authentication Example

fromscsimportSCS, AuthenticationErrorclassAuthService:
def__init__(self):
self.scs=SCS({
'api_key': 'your-api-key',
'project_id': 'your-project-id',
'base_url': 'https://your-scs-instance.com'
})
defregister(self, email: str, password: str, display_name: str) ->dict:
"""Register a new user with email verification."""user=self.scs.auth.register(
email=email,
password=password,
display_name=display_name
)
# Send verification emailself.scs.auth.send_email_verification()
returnuserdeflogin(self, email: str, password: str) ->dict:
"""Login with email and password."""returnself.scs.auth.login(email=email, password=password)
defsocial_login(self, provider: str, credentials: dict) ->dict:
"""Login with any social provider."""methods= {
'google': lambda: self.scs.auth.sign_in_with_google(**credentials),
'facebook': lambda: self.scs.auth.sign_in_with_facebook(**credentials),
'apple': lambda: self.scs.auth.sign_in_with_apple(**credentials),
'github': lambda: self.scs.auth.sign_in_with_github(**credentials),
'twitter': lambda: self.scs.auth.sign_in_with_twitter(**credentials),
'microsoft': lambda: self.scs.auth.sign_in_with_microsoft(**credentials)
}
ifprovidernotinmethods:
raiseValueError(f"Unknown provider: {provider}")
returnmethods[provider]()
defcontinue_as_guest(self, custom_data: dict=None) ->dict:
"""Sign in anonymously for guest access."""returnself.scs.auth.sign_in_anonymously(custom_data=custom_dataor {})
defupgrade_guest_account(self, provider: str, credentials: dict) ->dict:
"""Upgrade anonymous account to permanent."""user=self.scs.auth.get_current_user()
ifnotuserornotuser.get('isAnonymous'):
raiseValueError('Current user is not anonymous')
returnself.scs.auth.link_provider(provider, credentials)
deflogout(self):
"""Sign out the current user."""self.scs.auth.logout()
# Usageauth=AuthService()
# Register new useruser=auth.register('user@example.com', 'password123', 'John Doe')
# Or sign in with Googlegoogle_user=auth.social_login('google', {'id_token': 'xxx'})
# Or continue as guest and upgrade laterguest=auth.continue_as_guest({'source': 'homepage'})
# ... user decides to create account ...upgraded=auth.upgrade_guest_account('google', {'idToken': 'xxx'})

Database

Document database with query builder. SCS supports two powerful database options:

Database Types

TypeNameDescriptionBest For
eazieaZI DatabaseDocument-based NoSQL with Firestore-like collections, documents, and subcollectionsDevelopment, prototyping, small to medium apps
reladbRelaDBProduction-grade NoSQL database with relational-style viewsProduction, scalability, advanced queries

Initialize with eaZI (Default)

fromscsimportSCS# eaZI is the default database - no special configuration neededscs=SCS({
'project_id': 'your-project-id',
'api_key': 'your-api-key'# database_type: 'eazi' is implicit
})

Initialize with RelaDB (Production)

fromscsimportSCS# Use RelaDB for productionscs=SCS({
'project_id': 'your-project-id',
'api_key': 'your-api-key',
'database_type': 'reladb'# Enable RelaDB
})

eaZI Database Features

  • Document-based: Firestore-like collections and documents
  • Subcollections: Nested data organization
  • File-based storage: No external dependencies required
  • Zero configuration: Works out of the box
  • Query support: Filtering, ordering, and pagination

RelaDB Features

  • Production-ready: Built for reliability and performance
  • Scalable: Horizontal scaling and replication support
  • Advanced queries: Aggregation pipelines, complex filters
  • Indexing: Custom indexes for optimized performance
  • Schema flexibility: Dynamic schema with validation support
  • Relational-style views: Table view with columns and rows in the console

Collection Operations

# Get a collection referenceusers=scs.database.collection('users')
# List all collectionscollections=scs.database.list_collections()
# Create a collectionscs.database.create_collection('new_collection')
# Delete a collectionscs.database.delete_collection('old_collection')

Document Operations

# Add document with auto-generated IDdoc=scs.database.collection('users').add({
'name': 'John Doe',
'email': 'john@example.com',
'age': 30,
'tags': ['developer', 'python'],
'profile': {
'bio': 'Software developer',
'avatar': 'https://example.com/avatar.jpg'
}
})
print(f'Document ID: {doc["id"]}')
# Set document with custom ID (creates or overwrites)scs.database.collection('users').doc('user-123').set({
'name': 'Jane Doe',
'email': 'jane@example.com'
})
# Get a single documentuser=scs.database.collection('users').doc('user-123').get()
print(user)
# Update document (partial update)scs.database.collection('users').doc('user-123').update({
'age': 31,
'profile.bio': 'Senior developer'
})
# Delete documentscs.database.collection('users').doc('user-123').delete()

Query Operations

# Simple query with single filteractive_users=scs.database.collection('users') \
.where('status', '==', 'active') \
.get()
# Multiple filtersresults=scs.database.collection('users') \
.where('age', '>=', 18) \
.where('status', '==', 'active') \
.get()
# Ordering and paginationposts=scs.database.collection('posts') \
.where('published', '==', True) \
.order_by('created_at', 'desc') \
.limit(10) \
.skip(20) \
.get()
# Using 'in' operatorfeatured=scs.database.collection('posts') \
.where('category', 'in', ['tech', 'science', 'news']) \
.get()
# Using 'contains' for array fieldstagged=scs.database.collection('posts') \
.where('tags', 'contains', 'python') \
.get()

Query Operators

OperatorDescriptionExample
==Equal to.where('status', '==', 'active')
!=Not equal to.where('status', '!=', 'deleted')
>Greater than.where('age', '>', 18)
>=Greater than or equal.where('age', '>=', 18)
<Less than.where('price', '<', 100)
<=Less than or equal.where('price', '<=', 50)
inValue in array.where('status', 'in', ['active', 'pending'])
containsArray contains value.where('tags', 'contains', 'featured')

Subcollections

# Access a subcollectionposts_ref=scs.database \
.collection('users') \
.doc('user_id') \
.collection('posts')
# Add to subcollectionpost=posts_ref.add({
'title': 'My First Post',
'content': 'Hello World!',
'created_at': datetime.now().isoformat()
})
# Query subcollectionuser_posts=posts_ref \
.order_by('created_at', 'desc') \
.limit(5) \
.get()
# Nested subcollections (e.g., users/user_id/posts/post_id/comments)comments_ref=scs.database \
.collection('users') \
.doc('user_id') \
.collection('posts') \
.doc('post_id') \
.collection('comments')

Storage

# Upload from file pathfile_info=scs.storage.upload('./image.png')
# Upload with folderfile_info=scs.storage.upload('./doc.pdf', folder='documents')
# Upload from bytesfile_info=scs.storage.upload_bytes(
data=image_bytes,
filename='image.png',
content_type='image/png'
)
# List filesfiles=scs.storage.list_files(folder='images')
# Get file referencefile_ref=scs.storage.ref('file-id')
# Download filecontent=file_ref.download()
# Download to filefile_ref.download_to_file('./local_file.png')
# Get metadatametadata=file_ref.get_metadata()
# Delete filefile_ref.delete()
# Folder operationsscs.storage.create_folder('my-folder')
scs.storage.delete_folder('my-folder')

Realtime Database

Real-time data synchronization via WebSocket:

# Connect to realtime servicescs.realtime.connect()
# Get referencechat_ref=scs.realtime.ref('chat/room1')
# Subscribe to updatesdefon_message(data, event):
print(f'Event: {event}, Data: {data}')
unsubscribe=chat_ref.on(on_message)
# Subscribe to single updatechat_ref.once(lambdadata, event: print(data))
# Unsubscribeunsubscribe()
# Disconnectscs.realtime.disconnect()

Cloud Messaging

Push notifications with topics and direct messaging:

# Register device tokenscs.messaging.register_token(
token='device-fcm-token',
platform='android'
)
# Topic operationsscs.messaging.create_topic('news', description='News updates')
scs.messaging.subscribe_to_topic('news', 'device-token')
scs.messaging.unsubscribe_from_topic('news', 'device-token')
# Send to topicscs.messaging.send_to_topic(
topic='news',
title='Breaking News',
body='Something happened!',
data={'articleId': '123'}
)
# Send to specific devicescs.messaging.send_to_token(
token='device-token',
title='Personal Alert',
body='You have a new message'
)
# Send to multiple devicesscs.messaging.send_to_tokens(
tokens=['token1', 'token2'],
title='Broadcast',
body='Hello everyone!'
)

Remote Config

Dynamic app configuration:

# Fetch configurationconfig=scs.remote_config.fetch()
# Get values with type safetytheme=scs.remote_config.get_string('app_theme', default='light')
max_items=scs.remote_config.get_int('max_items', default=10)
enabled=scs.remote_config.get_bool('feature_enabled', default=False)
settings=scs.remote_config.get_json('settings', default={})
# Admin: Manage parametersscs.remote_config.create_param(
key='app_theme',
value='dark',
description='Default app theme'
)
scs.remote_config.update_param('app_theme', 'light')
scs.remote_config.delete_param('old_param')
# Publish changesscs.remote_config.publish()
# Version managementversions=scs.remote_config.list_versions()
scs.remote_config.rollback('version-id')

Serverless Functions

Execute custom code on the backend:

# Invoke a functionresult=scs.functions.invoke('processOrder', {
'orderId': '12345',
'action': 'confirm'
})
# Admin: Create a functionscs.functions.create(
name='processOrder',
code=''' module.exports = async (data, context) => { const { orderId, action } = data; // Process order... return { success: true, orderId }; }; ''',
runtime='nodejs18',
timeout=30000,
memory=256
)
# Admin: List functionsfunctions=scs.functions.list()
# Admin: Update functionscs.functions.update('function-id', code='...')
# Admin: Test functionresult=scs.functions.test('function-id', {'test': 'data'})
# Admin: Get logslogs=scs.functions.get_logs('function-id')

AI

Chat, text completion, and image generation with local LLM models:

# Chat with AIresponse=scs.ai.chat(
message="What is the capital of France?",
system_prompt="You are a helpful geography assistant."
)
print(response['content'])
# Text completionresponse=scs.ai.complete(prompt="Once upon a time")
print(response['content'])
# Generate imageresponse=scs.ai.generate_image(prompt="A sunset over mountains")
print(response['imageUrl'])
# List available modelsmodels=scs.ai.list_models()
# Conversation managementconversation=scs.ai.create_conversation(title="Geography Chat")
scs.ai.get_conversation(conversation['conversationId'])
scs.ai.delete_conversation(conversation['conversationId'])

AI Agents

Create and manage AI agents with custom instructions and tools:

# Create an agentagent=scs.ai.create_agent(
name="Customer Support",
instructions="You are a helpful customer support assistant. Be polite and helpful.",
model="llama3.2",
temperature=0.7
)
print(f"Created agent: {agent['agentId']}")
# List agentsagents=scs.ai.list_agents()
# Run the agentresponse=scs.ai.run_agent(
agent_id=agent['agentId'],
input="How do I reset my password?"
)
print(f"Agent: {response['output']}")
print(f"Session: {response['sessionId']}")
# Continue the conversation in the same sessionresponse=scs.ai.run_agent(
agent_id=agent['agentId'],
input="Thanks! What about enabling 2FA?",
session_id=response['sessionId']
)
# List agent sessionssessions=scs.ai.list_agent_sessions(agent['agentId'])
# Get full session historysession=scs.ai.get_agent_session(agent['agentId'], response['sessionId'])
formsginsession['messages']:
print(f"{msg['role']}: {msg['content']}")
# Update agentscs.ai.update_agent(
agent_id=agent['agentId'],
instructions="Updated instructions here",
temperature=0.5
)
# Define a tool for agentstool=scs.ai.define_tool(
name="get_weather",
description="Get weather for a location",
parameters={
"type": "object",
"properties": {
"location": {"type": "string", "description": "City name"}
}
}
)
# List toolstools=scs.ai.list_tools()
# Delete agent and sessionsscs.ai.delete_agent_session(agent['agentId'], response['sessionId'])
scs.ai.delete_agent(agent['agentId'])

Configuration File

Create an scs-info.json file:

{
"sdk_config": {
"api_key": "pk_your_api_key",
"project_id": "your-project-id",
"base_url": "https://your-scs-instance.com"
}
}

Error Handling

fromscsimportSCS, SCSError, AuthenticationError, NotFoundError, ValidationErrortry:
user=scs.auth.login(email='user@example.com', password='wrong')
exceptAuthenticationErrorase:
print(f'Auth failed: {e.message}')
print(f'Status: {e.status}')
exceptNotFoundErrorase:
print(f'Not found: {e.message}')
exceptValidationErrorase:
print(f'Validation error: {e.message}')
exceptSCSErrorase:
print(f'SCS error: {e.message}')

Context Manager

withSCS({'api_key': '...', 'project_id': '...'}) asscs:
users=scs.database.collection('users').get()
# Connection automatically closed

Requirements

  • Python 3.8+
  • requests
  • python-socketio[client] (optional, for realtime functionality)

License

MIT License

About

The official Python SDK for Spyxpo Cloud Services

Resources

Code of conduct

Contributing

Security policy

Stars

2 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Latest commit

History

13 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

SCS Python SDK

Python SDK for SCS (Spyxpo Cloud Services)

Installation

pip install scs-sdk

For realtime functionality (WebSocket support):

pip install scs-sdk[realtime]

Quick Start

fromscsimportSCS# Initialize from config filescs=SCS.initialize_app('./scs-info.json')
# Or initialize with config dictscs=SCS({
'api_key': 'your-api-key',
'project_id': 'your-project-id',
'base_url': 'https://your-scs-instance.com'
})
# Authenticationuser=scs.auth.login(email='user@example.com', password='password123')
# Databaseusers=scs.database.collection('users').where('age', '>=', 18).limit(10).get()
# Storagescs.storage.upload('./image.png', folder='images')

Services

Authentication

# Register a new useruser=scs.auth.register(
email='user@example.com',
password='password123',
display_name='John Doe'
)
# Loginuser=scs.auth.login(email='user@example.com', password='password123')
# Get current userme=scs.auth.get_current_user()
# Update profilescs.auth.update_profile(display_name='Jane Doe')
# Change passwordscs.auth.change_password(
current_password='old123',
new_password='new456'
)
# Logoutscs.auth.logout()

OAuth and Social Sign-In

SCS supports multiple OAuth providers for seamless social authentication. Each provider follows a similar pattern but requires different credentials obtained from their respective SDKs.

Google Sign-In

Authenticate users with their Google account. Requires the Google Sign-In SDK on the client.

# Sign in with Googleuser=scs.auth.sign_in_with_google(
id_token='google-id-token', # Required: ID token from Google Sign-Inaccess_token='google-access-token'# Optional: Access token for additional scopes
)
print(f"User ID: {user['uid']}")
print(f"Email: {user['email']}")
print(f"Display Name: {user['displayName']}")
print(f"Photo URL: {user['photoURL']}")
print(f"Provider: {user['providerId']}") # 'google'

Parameters:

ParameterTypeRequiredDescription
id_tokenstrYesThe ID token obtained from Google Sign-In SDK
access_tokenstrNoAccess token for additional Google API scopes

Returns:dict with user data and token

Example with Google OAuth Library:

fromgoogle.oauth2importid_tokenfromgoogle.auth.transportimportrequests# Verify and get token on server sidedefverify_google_token(token):
try:
idinfo=id_token.verify_oauth2_token(
token, requests.Request(), GOOGLE_CLIENT_ID
)
returnidinfoexceptValueError:
returnNone# Sign in with the tokenuser=scs.auth.sign_in_with_google(id_token=google_token)
Facebook Sign-In

Authenticate users with their Facebook account.

# Sign in with Facebookuser=scs.auth.sign_in_with_facebook(
access_token='facebook-access-token'# Required: Access token from Facebook Login
)
print(f"User: {user['displayName']}")
print(f"Email: {user['email']}") # May be None if user didn't grant email permission

Parameters:

ParameterTypeRequiredDescription
access_tokenstrYesAccess token from Facebook Login SDK

Example with Facebook SDK:

# After getting access token from Facebook SDK on clientdeffacebook_login(access_token):
try:
user=scs.auth.sign_in_with_facebook(access_token=access_token)
returnuserexceptExceptionase:
print(f"Facebook login failed: {e}")
returnNone
Apple Sign-In

Authenticate users with their Apple ID. Ideal for iOS apps and required for apps with social login on the App Store.

# Sign in with Appleuser=scs.auth.sign_in_with_apple(
identity_token='apple-identity-token', # Required: Identity token from Sign in with Appleauthorization_code='apple-auth-code', # Optional: Authorization code for server verificationfull_name='John Doe'# Optional: User's name (only available on first sign-in)
)

Parameters:

ParameterTypeRequiredDescription
identity_tokenstrYesJWT identity token from Sign in with Apple
authorization_codestrNoAuthorization code for additional verification
full_namestrNoUser's full name (Apple only provides this on first sign-in)

Important Notes:

  • Apple only provides the user's name on the first sign-in. Store it immediately.
  • Users can choose to hide their email (Apple provides a relay email).
  • Required for apps using social login on iOS/macOS.
GitHub Sign-In

Authenticate users with their GitHub account. Popular for developer-focused applications.

# Sign in with GitHubuser=scs.auth.sign_in_with_github(
code='github-oauth-code', # Required: OAuth authorization coderedirect_uri='https://yourapp.com/callback'# Optional: Must match OAuth app settings
)
print(f"GitHub username: {user['displayName']}")
print(f"Email: {user['email']}")

Parameters:

ParameterTypeRequiredDescription
codestrYesOAuth authorization code from GitHub OAuth flow
redirect_uristrNoRedirect URI (must match your GitHub OAuth App settings)

OAuth Flow Example (Flask):

fromflaskimportFlask, redirect, requestimportrequestsapp=Flask(__name__)
GITHUB_CLIENT_ID='your-client-id'GITHUB_CLIENT_SECRET='your-client-secret'@app.route('/login/github')defgithub_login():
returnredirect(
f'https://github.com/login/oauth/authorize'f'?client_id={GITHUB_CLIENT_ID}'f'&redirect_uri=https://yourapp.com/callback'f'&scope=read:user user:email'
)
@app.route('/callback')defgithub_callback():
code=request.args.get('code')
ifcode:
user=scs.auth.sign_in_with_github(
code=code,
redirect_uri='https://yourapp.com/callback'
)
returnf"Welcome, {user['displayName']}!"return"Login failed"
Twitter/X Sign-In

Authenticate users with their Twitter/X account using OAuth 1.0a.

# Sign in with Twitter/Xuser=scs.auth.sign_in_with_twitter(
oauth_token='twitter-oauth-token', # Required: OAuth tokenoauth_token_secret='twitter-oauth-secret'# Required: OAuth token secret
)

Parameters:

ParameterTypeRequiredDescription
oauth_tokenstrYesOAuth token from Twitter authentication
oauth_token_secretstrYesOAuth token secret from Twitter authentication

Note: Twitter uses OAuth 1.0a which requires a more complex flow. Consider using a library like tweepy or python-twitter.

Microsoft Sign-In

Authenticate users with their Microsoft account (personal, work, or school accounts).

# Sign in with Microsoftuser=scs.auth.sign_in_with_microsoft(
access_token='microsoft-access-token', # Required: Access token from MSALid_token='microsoft-id-token'# Optional: ID token for additional claims
)

Parameters:

ParameterTypeRequiredDescription
access_tokenstrYesAccess token from Microsoft Authentication Library (MSAL)
id_tokenstrNoID token for additional user claims

Example with MSAL Python:

frommsalimportConfidentialClientApplicationapp=ConfidentialClientApplication(
client_id="your-client-id",
authority="https://login.microsoftonline.com/common",
client_credential="your-client-secret"
)
# After getting tokens from authorization code flowresult=app.acquire_token_by_authorization_code(
code,
scopes=["User.Read"],
redirect_uri="https://yourapp.com/callback"
)
if"access_token"inresult:
user=scs.auth.sign_in_with_microsoft(
access_token=result["access_token"],
id_token=result.get("id_token")
)

Anonymous Authentication

Allow users to use your app without creating an account. Anonymous accounts can later be upgraded to permanent accounts by linking a provider.

# Sign in anonymously (creates a temporary account)user=scs.auth.sign_in_anonymously(
custom_data={'referrer': 'landing-page', 'campaign': 'summer-sale'} # optional
)
print(f"Anonymous user ID: {user['uid']}")
print(f"Is anonymous: {user['isAnonymous']}") # True

Parameters:

ParameterTypeRequiredDescription
custom_datadictNoCustom data to store with the anonymous user for analytics

Use Cases:

  • Allow users to try your app before signing up
  • Guest checkout in e-commerce
  • Save user progress/preferences before account creation
  • A/B testing with user tracking

Converting Anonymous to Permanent Account:

# User decides to create a permanent account# Link their anonymous account to a providertry:
user=scs.auth.link_provider('google', {
'idToken': 'google-id-token'
})
print("Account upgraded! User data preserved.")
print(f"Is anonymous: {user['isAnonymous']}") # FalseexceptExceptionase:
if'credential-already-in-use'instr(e):
# This Google account is already linked to another userprint("This account is already registered. Please sign in instead.")
else:
raise

Phone Number Authentication

Two-step authentication flow using SMS verification codes.

# Step 1: Send verification code to phoneresult=scs.auth.send_phone_verification_code(
phone_number='+1234567890', # Required: E.164 formatrecaptcha_token='recaptcha-token'# Optional: For bot protection
)
verification_id=result['verificationId']
print(f"Verification ID: {verification_id}")
# Store this ID - you'll need it in step 2# Step 2: User enters the code they receiveduser=scs.auth.sign_in_with_phone_number(
verification_id=verification_id, # The ID from step 1code='123456'# 6-digit code from SMS
)
print(f"Phone verified: {user['phoneNumber']}")

send_phone_verification_code Parameters:

ParameterTypeRequiredDescription
phone_numberstrYesPhone number in E.164 format (e.g., +1234567890)
recaptcha_tokenstrNoreCAPTCHA token for abuse prevention

sign_in_with_phone_number Parameters:

ParameterTypeRequiredDescription
verification_idstrYesVerification ID from send_phone_verification_code
codestrYes6-digit verification code from SMS

Complete Flow Example:

defsign_in_with_phone(phone_number: str, get_code_callback) ->dict:
""" Sign in with phone number. Args: phone_number: Phone number in E.164 format get_code_callback: Function that prompts user for the SMS code Returns: User dict on success """try:
# Step 1: Send coderesult=scs.auth.send_phone_verification_code(
phone_number=phone_number
)
verification_id=result['verificationId']
# Get code from user (your UI implementation)code=get_code_callback()
# Step 2: Verify codeuser=scs.auth.sign_in_with_phone_number(
verification_id=verification_id,
code=code
)
returnuserexceptExceptionase:
error_msg=str(e).lower()
if'invalid-phone-number'inerror_msg:
raiseValueError('Invalid phone number format. Use E.164 format (+1234567890)')
elif'too-many-requests'inerror_msg:
raiseValueError('Too many attempts. Please try again later.')
elif'invalid-verification-code'inerror_msg:
raiseValueError('Invalid verification code. Please try again.')
elif'code-expired'inerror_msg:
raiseValueError('Code expired. Please request a new one.')
else:
raise

Custom Token Authentication

Sign in using a JWT token generated by your own backend. Useful for migrating users from another system or integrating with custom authentication.

# Sign in with a custom token (generated by your backend)user=scs.auth.sign_in_with_custom_token('your-custom-jwt-token')
print(f"Signed in user: {user['uid']}")

Parameters:

ParameterTypeRequiredDescription
tokenstrYesJWT token generated by your backend

Backend Token Generation Example:

importjwtimporttimeimportosdefcreate_custom_token(uid: str, claims: dict=None) ->str:
""" Create a custom authentication token. Args: uid: Unique user identifier claims: Optional custom claims to include Returns: JWT token string """payload= {
'uid': uid,
'claims': claimsor {},
'iat': int(time.time()),
'exp': int(time.time()) +3600# 1 hour
}
returnjwt.encode(
payload,
os.environ['SCS_SECRET_KEY'],
algorithm='HS256'
)
# Generate token for a usercustom_token=create_custom_token('user-123', {'role': 'admin'})
# Send this token to the client for sign-in

Use Cases:

  • Migrating users from another authentication system
  • Server-side user creation with immediate client sign-in
  • Integration with enterprise SSO systems (SAML, LDAP)
  • Machine-to-machine authentication

Account Linking

Link multiple authentication providers to a single account. Users can sign in with any linked provider.

# Link a provider to current accountuser=scs.auth.link_provider('facebook', {
'accessToken': 'facebook-access-token'
})
print(f"Linked providers: {user['providerData']}")
# [{'providerId': 'password'}, {'providerId': 'google'}, {'providerId': 'facebook'}]# Unlink a provider from current accountuser=scs.auth.unlink_provider('facebook')
print(f"Remaining providers: {user['providerData']}")
# Get available sign-in methods for an emailresult=scs.auth.fetch_sign_in_methods_for_email('user@example.com')
print(f"Available methods: {result['methods']}")
# ['password', 'google', 'facebook']

link_provider Parameters:

ParameterTypeRequiredDescription
providerstrYesProvider name: 'google', 'facebook', 'apple', 'github', 'twitter', 'microsoft'
credentialsdictYesProvider-specific credentials (tokens)

Supported Providers and Credentials:

ProviderRequired Credentials
google{'idToken': '...', 'accessToken': '...'} (accessToken optional)
facebook{'accessToken': '...'}
apple{'identityToken': '...', 'authorizationCode': '...', 'fullName': '...'}
github{'code': '...', 'redirectUri': '...'}
twitter{'oauthToken': '...', 'oauthTokenSecret': '...'}
microsoft{'accessToken': '...', 'idToken': '...'}

Complete Account Linking Flow:

defcan_link_provider(email: str, provider: str) ->bool:
"""Check if a provider can be linked to an account."""result=scs.auth.fetch_sign_in_methods_for_email(email)
methods=result.get('methods', [])
ifproviderinmethods:
raiseValueError(f"{provider} is already linked to this account")
returnTruedeflink_google_account(google_id_token: str) ->dict:
"""Link Google to the current user's account."""try:
# Verify user is signed incurrent_user=scs.auth.get_current_user()
ifnotcurrent_user:
raiseValueError('Must be signed in to link accounts')
# Link the provideruser=scs.auth.link_provider('google', {
'idToken': google_id_token
})
print('Successfully linked Google account')
returnuserexceptExceptionase:
error_msg=str(e).lower()
if'credential-already-in-use'inerror_msg:
print('This Google account is already linked to another user')
elif'provider-already-linked'inerror_msg:
print('Google is already linked to this account')
raise

Password Reset & Email Verification

Handle password recovery and email verification flows.

# Send password reset emailscs.auth.send_password_reset_email('user@example.com')
print("Password reset email sent")
# Confirm password reset (user clicks link in email, you extract the code)scs.auth.confirm_password_reset(
code='reset-code-from-email', # Code from the reset linknew_password='newSecurePassword123'# User's new password
)
print("Password successfully reset")
# Send email verification to current userscs.auth.send_email_verification()
print("Verification email sent")
# Verify email with code (user clicks link, you extract the code)scs.auth.verify_email('verification-code')
print("Email verified")

Error Handling:

defreset_password(email: str) ->dict:
"""Send password reset email with error handling."""try:
scs.auth.send_password_reset_email(email)
return {'success': True, 'message': 'Reset email sent'}
exceptExceptionase:
error_msg=str(e).lower()
if'user-not-found'inerror_msg:
# Don't reveal if user exists for securityreturn {'success': True, 'message': 'If this email exists, a reset link was sent'}
elif'too-many-requests'inerror_msg:
return {'success': False, 'message': 'Too many attempts. Please try later.'}
else:
raisedefconfirm_reset(code: str, new_password: str) ->dict:
"""Confirm password reset with validation."""# Validate password strengthiflen(new_password) <8:
raiseValueError('Password must be at least 8 characters')
try:
scs.auth.confirm_password_reset(code=code, new_password=new_password)
return {'success': True}
exceptExceptionase:
error_msg=str(e).lower()
if'expired-action-code'inerror_msg:
return {'success': False, 'message': 'Reset link expired. Please request a new one.'}
elif'invalid-action-code'inerror_msg:
return {'success': False, 'message': 'Invalid reset link.'}
elif'weak-password'inerror_msg:
return {'success': False, 'message': 'Password is too weak.'}
else:
raise

Authentication State Management

Manage user sessions and authentication state.

# Check if user is logged inis_logged_in=scs.auth.is_logged_inprint(f"Is logged in: {is_logged_in}")
# Get current user (from cache)cached_user=scs.auth.current_user# Get current user (fresh from server)user=scs.auth.get_current_user()
# Refresh user datarefreshed_user=scs.auth.reload()
# Get the current auth tokentoken=scs.auth.token

Complete Authentication Example

fromscsimportSCS, AuthenticationErrorclassAuthService:
def__init__(self):
self.scs=SCS({
'api_key': 'your-api-key',
'project_id': 'your-project-id',
'base_url': 'https://your-scs-instance.com'
})
defregister(self, email: str, password: str, display_name: str) ->dict:
"""Register a new user with email verification."""user=self.scs.auth.register(
email=email,
password=password,
display_name=display_name
)
# Send verification emailself.scs.auth.send_email_verification()
returnuserdeflogin(self, email: str, password: str) ->dict:
"""Login with email and password."""returnself.scs.auth.login(email=email, password=password)
defsocial_login(self, provider: str, credentials: dict) ->dict:
"""Login with any social provider."""methods= {
'google': lambda: self.scs.auth.sign_in_with_google(**credentials),
'facebook': lambda: self.scs.auth.sign_in_with_facebook(**credentials),
'apple': lambda: self.scs.auth.sign_in_with_apple(**credentials),
'github': lambda: self.scs.auth.sign_in_with_github(**credentials),
'twitter': lambda: self.scs.auth.sign_in_with_twitter(**credentials),
'microsoft': lambda: self.scs.auth.sign_in_with_microsoft(**credentials)
}
ifprovidernotinmethods:
raiseValueError(f"Unknown provider: {provider}")
returnmethods[provider]()
defcontinue_as_guest(self, custom_data: dict=None) ->dict:
"""Sign in anonymously for guest access."""returnself.scs.auth.sign_in_anonymously(custom_data=custom_dataor {})
defupgrade_guest_account(self, provider: str, credentials: dict) ->dict:
"""Upgrade anonymous account to permanent."""user=self.scs.auth.get_current_user()
ifnotuserornotuser.get('isAnonymous'):
raiseValueError('Current user is not anonymous')
returnself.scs.auth.link_provider(provider, credentials)
deflogout(self):
"""Sign out the current user."""self.scs.auth.logout()
# Usageauth=AuthService()
# Register new useruser=auth.register('user@example.com', 'password123', 'John Doe')
# Or sign in with Googlegoogle_user=auth.social_login('google', {'id_token': 'xxx'})
# Or continue as guest and upgrade laterguest=auth.continue_as_guest({'source': 'homepage'})
# ... user decides to create account ...upgraded=auth.upgrade_guest_account('google', {'idToken': 'xxx'})

Database

Document database with query builder. SCS supports two powerful database options:

Database Types

TypeNameDescriptionBest For
eazieaZI DatabaseDocument-based NoSQL with Firestore-like collections, documents, and subcollectionsDevelopment, prototyping, small to medium apps
reladbRelaDBProduction-grade NoSQL database with relational-style viewsProduction, scalability, advanced queries

Initialize with eaZI (Default)

fromscsimportSCS# eaZI is the default database - no special configuration neededscs=SCS({
'project_id': 'your-project-id',
'api_key': 'your-api-key'# database_type: 'eazi' is implicit
})

Initialize with RelaDB (Production)

fromscsimportSCS# Use RelaDB for productionscs=SCS({
'project_id': 'your-project-id',
'api_key': 'your-api-key',
'database_type': 'reladb'# Enable RelaDB
})

eaZI Database Features

  • Document-based: Firestore-like collections and documents
  • Subcollections: Nested data organization
  • File-based storage: No external dependencies required
  • Zero configuration: Works out of the box
  • Query support: Filtering, ordering, and pagination

RelaDB Features

  • Production-ready: Built for reliability and performance
  • Scalable: Horizontal scaling and replication support
  • Advanced queries: Aggregation pipelines, complex filters
  • Indexing: Custom indexes for optimized performance
  • Schema flexibility: Dynamic schema with validation support
  • Relational-style views: Table view with columns and rows in the console

Collection Operations

# Get a collection referenceusers=scs.database.collection('users')
# List all collectionscollections=scs.database.list_collections()
# Create a collectionscs.database.create_collection('new_collection')
# Delete a collectionscs.database.delete_collection('old_collection')

Document Operations

# Add document with auto-generated IDdoc=scs.database.collection('users').add({
'name': 'John Doe',
'email': 'john@example.com',
'age': 30,
'tags': ['developer', 'python'],
'profile': {
'bio': 'Software developer',
'avatar': 'https://example.com/avatar.jpg'
}
})
print(f'Document ID: {doc["id"]}')
# Set document with custom ID (creates or overwrites)scs.database.collection('users').doc('user-123').set({
'name': 'Jane Doe',
'email': 'jane@example.com'
})
# Get a single documentuser=scs.database.collection('users').doc('user-123').get()
print(user)
# Update document (partial update)scs.database.collection('users').doc('user-123').update({
'age': 31,
'profile.bio': 'Senior developer'
})
# Delete documentscs.database.collection('users').doc('user-123').delete()

Query Operations

# Simple query with single filteractive_users=scs.database.collection('users') \
.where('status', '==', 'active') \
.get()
# Multiple filtersresults=scs.database.collection('users') \
.where('age', '>=', 18) \
.where('status', '==', 'active') \
.get()
# Ordering and paginationposts=scs.database.collection('posts') \
.where('published', '==', True) \
.order_by('created_at', 'desc') \
.limit(10) \
.skip(20) \
.get()
# Using 'in' operatorfeatured=scs.database.collection('posts') \
.where('category', 'in', ['tech', 'science', 'news']) \
.get()
# Using 'contains' for array fieldstagged=scs.database.collection('posts') \
.where('tags', 'contains', 'python') \
.get()

Query Operators

OperatorDescriptionExample
==Equal to.where('status', '==', 'active')
!=Not equal to.where('status', '!=', 'deleted')
>Greater than.where('age', '>', 18)
>=Greater than or equal.where('age', '>=', 18)
<Less than.where('price', '<', 100)
<=Less than or equal.where('price', '<=', 50)
inValue in array.where('status', 'in', ['active', 'pending'])
containsArray contains value.where('tags', 'contains', 'featured')

Subcollections

# Access a subcollectionposts_ref=scs.database \
.collection('users') \
.doc('user_id') \
.collection('posts')
# Add to subcollectionpost=posts_ref.add({
'title': 'My First Post',
'content': 'Hello World!',
'created_at': datetime.now().isoformat()
})
# Query subcollectionuser_posts=posts_ref \
.order_by('created_at', 'desc') \
.limit(5) \
.get()
# Nested subcollections (e.g., users/user_id/posts/post_id/comments)comments_ref=scs.database \
.collection('users') \
.doc('user_id') \
.collection('posts') \
.doc('post_id') \
.collection('comments')

Storage

# Upload from file pathfile_info=scs.storage.upload('./image.png')
# Upload with folderfile_info=scs.storage.upload('./doc.pdf', folder='documents')
# Upload from bytesfile_info=scs.storage.upload_bytes(
data=image_bytes,
filename='image.png',
content_type='image/png'
)
# List filesfiles=scs.storage.list_files(folder='images')
# Get file referencefile_ref=scs.storage.ref('file-id')
# Download filecontent=file_ref.download()
# Download to filefile_ref.download_to_file('./local_file.png')
# Get metadatametadata=file_ref.get_metadata()
# Delete filefile_ref.delete()
# Folder operationsscs.storage.create_folder('my-folder')
scs.storage.delete_folder('my-folder')

Realtime Database

Real-time data synchronization via WebSocket:

# Connect to realtime servicescs.realtime.connect()
# Get referencechat_ref=scs.realtime.ref('chat/room1')
# Subscribe to updatesdefon_message(data, event):
print(f'Event: {event}, Data: {data}')
unsubscribe=chat_ref.on(on_message)
# Subscribe to single updatechat_ref.once(lambdadata, event: print(data))
# Unsubscribeunsubscribe()
# Disconnectscs.realtime.disconnect()

Cloud Messaging

Push notifications with topics and direct messaging:

# Register device tokenscs.messaging.register_token(
token='device-fcm-token',
platform='android'
)
# Topic operationsscs.messaging.create_topic('news', description='News updates')
scs.messaging.subscribe_to_topic('news', 'device-token')
scs.messaging.unsubscribe_from_topic('news', 'device-token')
# Send to topicscs.messaging.send_to_topic(
topic='news',
title='Breaking News',
body='Something happened!',
data={'articleId': '123'}
)
# Send to specific devicescs.messaging.send_to_token(
token='device-token',
title='Personal Alert',
body='You have a new message'
)
# Send to multiple devicesscs.messaging.send_to_tokens(
tokens=['token1', 'token2'],
title='Broadcast',
body='Hello everyone!'
)

Remote Config

Dynamic app configuration:

# Fetch configurationconfig=scs.remote_config.fetch()
# Get values with type safetytheme=scs.remote_config.get_string('app_theme', default='light')
max_items=scs.remote_config.get_int('max_items', default=10)
enabled=scs.remote_config.get_bool('feature_enabled', default=False)
settings=scs.remote_config.get_json('settings', default={})
# Admin: Manage parametersscs.remote_config.create_param(
key='app_theme',
value='dark',
description='Default app theme'
)
scs.remote_config.update_param('app_theme', 'light')
scs.remote_config.delete_param('old_param')
# Publish changesscs.remote_config.publish()
# Version managementversions=scs.remote_config.list_versions()
scs.remote_config.rollback('version-id')

Serverless Functions

Execute custom code on the backend:

# Invoke a functionresult=scs.functions.invoke('processOrder', {
'orderId': '12345',
'action': 'confirm'
})
# Admin: Create a functionscs.functions.create(
name='processOrder',
code=''' module.exports = async (data, context) => { const { orderId, action } = data; // Process order... return { success: true, orderId }; }; ''',
runtime='nodejs18',
timeout=30000,
memory=256
)
# Admin: List functionsfunctions=scs.functions.list()
# Admin: Update functionscs.functions.update('function-id', code='...')
# Admin: Test functionresult=scs.functions.test('function-id', {'test': 'data'})
# Admin: Get logslogs=scs.functions.get_logs('function-id')

AI

Chat, text completion, and image generation with local LLM models:

# Chat with AIresponse=scs.ai.chat(
message="What is the capital of France?",
system_prompt="You are a helpful geography assistant."
)
print(response['content'])
# Text completionresponse=scs.ai.complete(prompt="Once upon a time")
print(response['content'])
# Generate imageresponse=scs.ai.generate_image(prompt="A sunset over mountains")
print(response['imageUrl'])
# List available modelsmodels=scs.ai.list_models()
# Conversation managementconversation=scs.ai.create_conversation(title="Geography Chat")
scs.ai.get_conversation(conversation['conversationId'])
scs.ai.delete_conversation(conversation['conversationId'])

AI Agents

Create and manage AI agents with custom instructions and tools:

# Create an agentagent=scs.ai.create_agent(
name="Customer Support",
instructions="You are a helpful customer support assistant. Be polite and helpful.",
model="llama3.2",
temperature=0.7
)
print(f"Created agent: {agent['agentId']}")
# List agentsagents=scs.ai.list_agents()
# Run the agentresponse=scs.ai.run_agent(
agent_id=agent['agentId'],
input="How do I reset my password?"
)
print(f"Agent: {response['output']}")
print(f"Session: {response['sessionId']}")
# Continue the conversation in the same sessionresponse=scs.ai.run_agent(
agent_id=agent['agentId'],
input="Thanks! What about enabling 2FA?",
session_id=response['sessionId']
)
# List agent sessionssessions=scs.ai.list_agent_sessions(agent['agentId'])
# Get full session historysession=scs.ai.get_agent_session(agent['agentId'], response['sessionId'])
formsginsession['messages']:
print(f"{msg['role']}: {msg['content']}")
# Update agentscs.ai.update_agent(
agent_id=agent['agentId'],
instructions="Updated instructions here",
temperature=0.5
)
# Define a tool for agentstool=scs.ai.define_tool(
name="get_weather",
description="Get weather for a location",
parameters={
"type": "object",
"properties": {
"location": {"type": "string", "description": "City name"}
}
}
)
# List toolstools=scs.ai.list_tools()
# Delete agent and sessionsscs.ai.delete_agent_session(agent['agentId'], response['sessionId'])
scs.ai.delete_agent(agent['agentId'])

Configuration File

Create an scs-info.json file:

{
"sdk_config": {
"api_key": "pk_your_api_key",
"project_id": "your-project-id",
"base_url": "https://your-scs-instance.com"
}
}

Error Handling

fromscsimportSCS, SCSError, AuthenticationError, NotFoundError, ValidationErrortry:
user=scs.auth.login(email='user@example.com', password='wrong')
exceptAuthenticationErrorase:
print(f'Auth failed: {e.message}')
print(f'Status: {e.status}')
exceptNotFoundErrorase:
print(f'Not found: {e.message}')
exceptValidationErrorase:
print(f'Validation error: {e.message}')
exceptSCSErrorase:
print(f'SCS error: {e.message}')

Context Manager

withSCS({'api_key': '...', 'project_id': '...'}) asscs:
users=scs.database.collection('users').get()
# Connection automatically closed

Requirements

  • Python 3.8+
  • requests
  • python-socketio[client] (optional, for realtime functionality)

License

MIT License

About

The official Python SDK for Spyxpo Cloud Services

Resources

Code of conduct

Contributing

Security policy

Stars

2 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Latest commit

History

13 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

SCS Python SDK

Python SDK for SCS (Spyxpo Cloud Services)

Installation

pip install scs-sdk

For realtime functionality (WebSocket support):

pip install scs-sdk[realtime]

Quick Start

fromscsimportSCS# Initialize from config filescs=SCS.initialize_app('./scs-info.json')
# Or initialize with config dictscs=SCS({
'api_key': 'your-api-key',
'project_id': 'your-project-id',
'base_url': 'https://your-scs-instance.com'
})
# Authenticationuser=scs.auth.login(email='user@example.com', password='password123')
# Databaseusers=scs.database.collection('users').where('age', '>=', 18).limit(10).get()
# Storagescs.storage.upload('./image.png', folder='images')

Services

Authentication

# Register a new useruser=scs.auth.register(
email='user@example.com',
password='password123',
display_name='John Doe'
)
# Loginuser=scs.auth.login(email='user@example.com', password='password123')
# Get current userme=scs.auth.get_current_user()
# Update profilescs.auth.update_profile(display_name='Jane Doe')
# Change passwordscs.auth.change_password(
current_password='old123',
new_password='new456'
)
# Logoutscs.auth.logout()

OAuth and Social Sign-In

SCS supports multiple OAuth providers for seamless social authentication. Each provider follows a similar pattern but requires different credentials obtained from their respective SDKs.

Google Sign-In

Authenticate users with their Google account. Requires the Google Sign-In SDK on the client.

# Sign in with Googleuser=scs.auth.sign_in_with_google(
id_token='google-id-token', # Required: ID token from Google Sign-Inaccess_token='google-access-token'# Optional: Access token for additional scopes
)
print(f"User ID: {user['uid']}")
print(f"Email: {user['email']}")
print(f"Display Name: {user['displayName']}")
print(f"Photo URL: {user['photoURL']}")
print(f"Provider: {user['providerId']}") # 'google'

Parameters:

ParameterTypeRequiredDescription
id_tokenstrYesThe ID token obtained from Google Sign-In SDK
access_tokenstrNoAccess token for additional Google API scopes

Returns:dict with user data and token

Example with Google OAuth Library:

fromgoogle.oauth2importid_tokenfromgoogle.auth.transportimportrequests# Verify and get token on server sidedefverify_google_token(token):
try:
idinfo=id_token.verify_oauth2_token(
token, requests.Request(), GOOGLE_CLIENT_ID
)
returnidinfoexceptValueError:
returnNone# Sign in with the tokenuser=scs.auth.sign_in_with_google(id_token=google_token)
Facebook Sign-In

Authenticate users with their Facebook account.

# Sign in with Facebookuser=scs.auth.sign_in_with_facebook(
access_token='facebook-access-token'# Required: Access token from Facebook Login
)
print(f"User: {user['displayName']}")
print(f"Email: {user['email']}") # May be None if user didn't grant email permission

Parameters:

ParameterTypeRequiredDescription
access_tokenstrYesAccess token from Facebook Login SDK

Example with Facebook SDK:

# After getting access token from Facebook SDK on clientdeffacebook_login(access_token):
try:
user=scs.auth.sign_in_with_facebook(access_token=access_token)
returnuserexceptExceptionase:
print(f"Facebook login failed: {e}")
returnNone
Apple Sign-In

Authenticate users with their Apple ID. Ideal for iOS apps and required for apps with social login on the App Store.

# Sign in with Appleuser=scs.auth.sign_in_with_apple(
identity_token='apple-identity-token', # Required: Identity token from Sign in with Appleauthorization_code='apple-auth-code', # Optional: Authorization code for server verificationfull_name='John Doe'# Optional: User's name (only available on first sign-in)
)

Parameters:

ParameterTypeRequiredDescription
identity_tokenstrYesJWT identity token from Sign in with Apple
authorization_codestrNoAuthorization code for additional verification
full_namestrNoUser's full name (Apple only provides this on first sign-in)

Important Notes:

  • Apple only provides the user's name on the first sign-in. Store it immediately.
  • Users can choose to hide their email (Apple provides a relay email).
  • Required for apps using social login on iOS/macOS.
GitHub Sign-In

Authenticate users with their GitHub account. Popular for developer-focused applications.

# Sign in with GitHubuser=scs.auth.sign_in_with_github(
code='github-oauth-code', # Required: OAuth authorization coderedirect_uri='https://yourapp.com/callback'# Optional: Must match OAuth app settings
)
print(f"GitHub username: {user['displayName']}")
print(f"Email: {user['email']}")

Parameters:

ParameterTypeRequiredDescription
codestrYesOAuth authorization code from GitHub OAuth flow
redirect_uristrNoRedirect URI (must match your GitHub OAuth App settings)

OAuth Flow Example (Flask):

fromflaskimportFlask, redirect, requestimportrequestsapp=Flask(__name__)
GITHUB_CLIENT_ID='your-client-id'GITHUB_CLIENT_SECRET='your-client-secret'@app.route('/login/github')defgithub_login():
returnredirect(
f'https://github.com/login/oauth/authorize'f'?client_id={GITHUB_CLIENT_ID}'f'&redirect_uri=https://yourapp.com/callback'f'&scope=read:user user:email'
)
@app.route('/callback')defgithub_callback():
code=request.args.get('code')
ifcode:
user=scs.auth.sign_in_with_github(
code=code,
redirect_uri='https://yourapp.com/callback'
)
returnf"Welcome, {user['displayName']}!"return"Login failed"
Twitter/X Sign-In

Authenticate users with their Twitter/X account using OAuth 1.0a.

# Sign in with Twitter/Xuser=scs.auth.sign_in_with_twitter(
oauth_token='twitter-oauth-token', # Required: OAuth tokenoauth_token_secret='twitter-oauth-secret'# Required: OAuth token secret
)

Parameters:

ParameterTypeRequiredDescription
oauth_tokenstrYesOAuth token from Twitter authentication
oauth_token_secretstrYesOAuth token secret from Twitter authentication

Note: Twitter uses OAuth 1.0a which requires a more complex flow. Consider using a library like tweepy or python-twitter.

Microsoft Sign-In

Authenticate users with their Microsoft account (personal, work, or school accounts).

# Sign in with Microsoftuser=scs.auth.sign_in_with_microsoft(
access_token='microsoft-access-token', # Required: Access token from MSALid_token='microsoft-id-token'# Optional: ID token for additional claims
)

Parameters:

ParameterTypeRequiredDescription
access_tokenstrYesAccess token from Microsoft Authentication Library (MSAL)
id_tokenstrNoID token for additional user claims

Example with MSAL Python:

frommsalimportConfidentialClientApplicationapp=ConfidentialClientApplication(
client_id="your-client-id",
authority="https://login.microsoftonline.com/common",
client_credential="your-client-secret"
)
# After getting tokens from authorization code flowresult=app.acquire_token_by_authorization_code(
code,
scopes=["User.Read"],
redirect_uri="https://yourapp.com/callback"
)
if"access_token"inresult:
user=scs.auth.sign_in_with_microsoft(
access_token=result["access_token"],
id_token=result.get("id_token")
)

Anonymous Authentication

Allow users to use your app without creating an account. Anonymous accounts can later be upgraded to permanent accounts by linking a provider.

# Sign in anonymously (creates a temporary account)user=scs.auth.sign_in_anonymously(
custom_data={'referrer': 'landing-page', 'campaign': 'summer-sale'} # optional
)
print(f"Anonymous user ID: {user['uid']}")
print(f"Is anonymous: {user['isAnonymous']}") # True

Parameters:

ParameterTypeRequiredDescription
custom_datadictNoCustom data to store with the anonymous user for analytics

Use Cases:

  • Allow users to try your app before signing up
  • Guest checkout in e-commerce
  • Save user progress/preferences before account creation
  • A/B testing with user tracking

Converting Anonymous to Permanent Account:

# User decides to create a permanent account# Link their anonymous account to a providertry:
user=scs.auth.link_provider('google', {
'idToken': 'google-id-token'
})
print("Account upgraded! User data preserved.")
print(f"Is anonymous: {user['isAnonymous']}") # FalseexceptExceptionase:
if'credential-already-in-use'instr(e):
# This Google account is already linked to another userprint("This account is already registered. Please sign in instead.")
else:
raise

Phone Number Authentication

Two-step authentication flow using SMS verification codes.

# Step 1: Send verification code to phoneresult=scs.auth.send_phone_verification_code(
phone_number='+1234567890', # Required: E.164 formatrecaptcha_token='recaptcha-token'# Optional: For bot protection
)
verification_id=result['verificationId']
print(f"Verification ID: {verification_id}")
# Store this ID - you'll need it in step 2# Step 2: User enters the code they receiveduser=scs.auth.sign_in_with_phone_number(
verification_id=verification_id, # The ID from step 1code='123456'# 6-digit code from SMS
)
print(f"Phone verified: {user['phoneNumber']}")

send_phone_verification_code Parameters:

ParameterTypeRequiredDescription
phone_numberstrYesPhone number in E.164 format (e.g., +1234567890)
recaptcha_tokenstrNoreCAPTCHA token for abuse prevention

sign_in_with_phone_number Parameters:

ParameterTypeRequiredDescription
verification_idstrYesVerification ID from send_phone_verification_code
codestrYes6-digit verification code from SMS

Complete Flow Example:

defsign_in_with_phone(phone_number: str, get_code_callback) ->dict:
""" Sign in with phone number. Args: phone_number: Phone number in E.164 format get_code_callback: Function that prompts user for the SMS code Returns: User dict on success """try:
# Step 1: Send coderesult=scs.auth.send_phone_verification_code(
phone_number=phone_number
)
verification_id=result['verificationId']
# Get code from user (your UI implementation)code=get_code_callback()
# Step 2: Verify codeuser=scs.auth.sign_in_with_phone_number(
verification_id=verification_id,
code=code
)
returnuserexceptExceptionase:
error_msg=str(e).lower()
if'invalid-phone-number'inerror_msg:
raiseValueError('Invalid phone number format. Use E.164 format (+1234567890)')
elif'too-many-requests'inerror_msg:
raiseValueError('Too many attempts. Please try again later.')
elif'invalid-verification-code'inerror_msg:
raiseValueError('Invalid verification code. Please try again.')
elif'code-expired'inerror_msg:
raiseValueError('Code expired. Please request a new one.')
else:
raise

Custom Token Authentication

Sign in using a JWT token generated by your own backend. Useful for migrating users from another system or integrating with custom authentication.

# Sign in with a custom token (generated by your backend)user=scs.auth.sign_in_with_custom_token('your-custom-jwt-token')
print(f"Signed in user: {user['uid']}")

Parameters:

ParameterTypeRequiredDescription
tokenstrYesJWT token generated by your backend

Backend Token Generation Example:

importjwtimporttimeimportosdefcreate_custom_token(uid: str, claims: dict=None) ->str:
""" Create a custom authentication token. Args: uid: Unique user identifier claims: Optional custom claims to include Returns: JWT token string """payload= {
'uid': uid,
'claims': claimsor {},
'iat': int(time.time()),
'exp': int(time.time()) +3600# 1 hour
}
returnjwt.encode(
payload,
os.environ['SCS_SECRET_KEY'],
algorithm='HS256'
)
# Generate token for a usercustom_token=create_custom_token('user-123', {'role': 'admin'})
# Send this token to the client for sign-in

Use Cases:

  • Migrating users from another authentication system
  • Server-side user creation with immediate client sign-in
  • Integration with enterprise SSO systems (SAML, LDAP)
  • Machine-to-machine authentication

Account Linking

Link multiple authentication providers to a single account. Users can sign in with any linked provider.

# Link a provider to current accountuser=scs.auth.link_provider('facebook', {
'accessToken': 'facebook-access-token'
})
print(f"Linked providers: {user['providerData']}")
# [{'providerId': 'password'}, {'providerId': 'google'}, {'providerId': 'facebook'}]# Unlink a provider from current accountuser=scs.auth.unlink_provider('facebook')
print(f"Remaining providers: {user['providerData']}")
# Get available sign-in methods for an emailresult=scs.auth.fetch_sign_in_methods_for_email('user@example.com')
print(f"Available methods: {result['methods']}")
# ['password', 'google', 'facebook']

link_provider Parameters:

ParameterTypeRequiredDescription
providerstrYesProvider name: 'google', 'facebook', 'apple', 'github', 'twitter', 'microsoft'
credentialsdictYesProvider-specific credentials (tokens)

Supported Providers and Credentials:

ProviderRequired Credentials
google{'idToken': '...', 'accessToken': '...'} (accessToken optional)
facebook{'accessToken': '...'}
apple{'identityToken': '...', 'authorizationCode': '...', 'fullName': '...'}
github{'code': '...', 'redirectUri': '...'}
twitter{'oauthToken': '...', 'oauthTokenSecret': '...'}
microsoft{'accessToken': '...', 'idToken': '...'}

Complete Account Linking Flow:

defcan_link_provider(email: str, provider: str) ->bool:
"""Check if a provider can be linked to an account."""result=scs.auth.fetch_sign_in_methods_for_email(email)
methods=result.get('methods', [])
ifproviderinmethods:
raiseValueError(f"{provider} is already linked to this account")
returnTruedeflink_google_account(google_id_token: str) ->dict:
"""Link Google to the current user's account."""try:
# Verify user is signed incurrent_user=scs.auth.get_current_user()
ifnotcurrent_user:
raiseValueError('Must be signed in to link accounts')
# Link the provideruser=scs.auth.link_provider('google', {
'idToken': google_id_token
})
print('Successfully linked Google account')
returnuserexceptExceptionase:
error_msg=str(e).lower()
if'credential-already-in-use'inerror_msg:
print('This Google account is already linked to another user')
elif'provider-already-linked'inerror_msg:
print('Google is already linked to this account')
raise

Password Reset & Email Verification

Handle password recovery and email verification flows.

# Send password reset emailscs.auth.send_password_reset_email('user@example.com')
print("Password reset email sent")
# Confirm password reset (user clicks link in email, you extract the code)scs.auth.confirm_password_reset(
code='reset-code-from-email', # Code from the reset linknew_password='newSecurePassword123'# User's new password
)
print("Password successfully reset")
# Send email verification to current userscs.auth.send_email_verification()
print("Verification email sent")
# Verify email with code (user clicks link, you extract the code)scs.auth.verify_email('verification-code')
print("Email verified")

Error Handling:

defreset_password(email: str) ->dict:
"""Send password reset email with error handling."""try:
scs.auth.send_password_reset_email(email)
return {'success': True, 'message': 'Reset email sent'}
exceptExceptionase:
error_msg=str(e).lower()
if'user-not-found'inerror_msg:
# Don't reveal if user exists for securityreturn {'success': True, 'message': 'If this email exists, a reset link was sent'}
elif'too-many-requests'inerror_msg:
return {'success': False, 'message': 'Too many attempts. Please try later.'}
else:
raisedefconfirm_reset(code: str, new_password: str) ->dict:
"""Confirm password reset with validation."""# Validate password strengthiflen(new_password) <8:
raiseValueError('Password must be at least 8 characters')
try:
scs.auth.confirm_password_reset(code=code, new_password=new_password)
return {'success': True}
exceptExceptionase:
error_msg=str(e).lower()
if'expired-action-code'inerror_msg:
return {'success': False, 'message': 'Reset link expired. Please request a new one.'}
elif'invalid-action-code'inerror_msg:
return {'success': False, 'message': 'Invalid reset link.'}
elif'weak-password'inerror_msg:
return {'success': False, 'message': 'Password is too weak.'}
else:
raise

Authentication State Management

Manage user sessions and authentication state.

# Check if user is logged inis_logged_in=scs.auth.is_logged_inprint(f"Is logged in: {is_logged_in}")
# Get current user (from cache)cached_user=scs.auth.current_user# Get current user (fresh from server)user=scs.auth.get_current_user()
# Refresh user datarefreshed_user=scs.auth.reload()
# Get the current auth tokentoken=scs.auth.token

Complete Authentication Example

fromscsimportSCS, AuthenticationErrorclassAuthService:
def__init__(self):
self.scs=SCS({
'api_key': 'your-api-key',
'project_id': 'your-project-id',
'base_url': 'https://your-scs-instance.com'
})
defregister(self, email: str, password: str, display_name: str) ->dict:
"""Register a new user with email verification."""user=self.scs.auth.register(
email=email,
password=password,
display_name=display_name
)
# Send verification emailself.scs.auth.send_email_verification()
returnuserdeflogin(self, email: str, password: str) ->dict:
"""Login with email and password."""returnself.scs.auth.login(email=email, password=password)
defsocial_login(self, provider: str, credentials: dict) ->dict:
"""Login with any social provider."""methods= {
'google': lambda: self.scs.auth.sign_in_with_google(**credentials),
'facebook': lambda: self.scs.auth.sign_in_with_facebook(**credentials),
'apple': lambda: self.scs.auth.sign_in_with_apple(**credentials),
'github': lambda: self.scs.auth.sign_in_with_github(**credentials),
'twitter': lambda: self.scs.auth.sign_in_with_twitter(**credentials),
'microsoft': lambda: self.scs.auth.sign_in_with_microsoft(**credentials)
}
ifprovidernotinmethods:
raiseValueError(f"Unknown provider: {provider}")
returnmethods[provider]()
defcontinue_as_guest(self, custom_data: dict=None) ->dict:
"""Sign in anonymously for guest access."""returnself.scs.auth.sign_in_anonymously(custom_data=custom_dataor {})
defupgrade_guest_account(self, provider: str, credentials: dict) ->dict:
"""Upgrade anonymous account to permanent."""user=self.scs.auth.get_current_user()
ifnotuserornotuser.get('isAnonymous'):
raiseValueError('Current user is not anonymous')
returnself.scs.auth.link_provider(provider, credentials)
deflogout(self):
"""Sign out the current user."""self.scs.auth.logout()
# Usageauth=AuthService()
# Register new useruser=auth.register('user@example.com', 'password123', 'John Doe')
# Or sign in with Googlegoogle_user=auth.social_login('google', {'id_token': 'xxx'})
# Or continue as guest and upgrade laterguest=auth.continue_as_guest({'source': 'homepage'})
# ... user decides to create account ...upgraded=auth.upgrade_guest_account('google', {'idToken': 'xxx'})

Database

Document database with query builder. SCS supports two powerful database options:

Database Types

TypeNameDescriptionBest For
eazieaZI DatabaseDocument-based NoSQL with Firestore-like collections, documents, and subcollectionsDevelopment, prototyping, small to medium apps
reladbRelaDBProduction-grade NoSQL database with relational-style viewsProduction, scalability, advanced queries

Initialize with eaZI (Default)

fromscsimportSCS# eaZI is the default database - no special configuration neededscs=SCS({
'project_id': 'your-project-id',
'api_key': 'your-api-key'# database_type: 'eazi' is implicit
})

Initialize with RelaDB (Production)

fromscsimportSCS# Use RelaDB for productionscs=SCS({
'project_id': 'your-project-id',
'api_key': 'your-api-key',
'database_type': 'reladb'# Enable RelaDB
})

eaZI Database Features

  • Document-based: Firestore-like collections and documents
  • Subcollections: Nested data organization
  • File-based storage: No external dependencies required
  • Zero configuration: Works out of the box
  • Query support: Filtering, ordering, and pagination

RelaDB Features

  • Production-ready: Built for reliability and performance
  • Scalable: Horizontal scaling and replication support
  • Advanced queries: Aggregation pipelines, complex filters
  • Indexing: Custom indexes for optimized performance
  • Schema flexibility: Dynamic schema with validation support
  • Relational-style views: Table view with columns and rows in the console

Collection Operations

# Get a collection referenceusers=scs.database.collection('users')
# List all collectionscollections=scs.database.list_collections()
# Create a collectionscs.database.create_collection('new_collection')
# Delete a collectionscs.database.delete_collection('old_collection')

Document Operations

# Add document with auto-generated IDdoc=scs.database.collection('users').add({
'name': 'John Doe',
'email': 'john@example.com',
'age': 30,
'tags': ['developer', 'python'],
'profile': {
'bio': 'Software developer',
'avatar': 'https://example.com/avatar.jpg'
}
})
print(f'Document ID: {doc["id"]}')
# Set document with custom ID (creates or overwrites)scs.database.collection('users').doc('user-123').set({
'name': 'Jane Doe',
'email': 'jane@example.com'
})
# Get a single documentuser=scs.database.collection('users').doc('user-123').get()
print(user)
# Update document (partial update)scs.database.collection('users').doc('user-123').update({
'age': 31,
'profile.bio': 'Senior developer'
})
# Delete documentscs.database.collection('users').doc('user-123').delete()

Query Operations

# Simple query with single filteractive_users=scs.database.collection('users') \
.where('status', '==', 'active') \
.get()
# Multiple filtersresults=scs.database.collection('users') \
.where('age', '>=', 18) \
.where('status', '==', 'active') \
.get()
# Ordering and paginationposts=scs.database.collection('posts') \
.where('published', '==', True) \
.order_by('created_at', 'desc') \
.limit(10) \
.skip(20) \
.get()
# Using 'in' operatorfeatured=scs.database.collection('posts') \
.where('category', 'in', ['tech', 'science', 'news']) \
.get()
# Using 'contains' for array fieldstagged=scs.database.collection('posts') \
.where('tags', 'contains', 'python') \
.get()

Query Operators

OperatorDescriptionExample
==Equal to.where('status', '==', 'active')
!=Not equal to.where('status', '!=', 'deleted')
>Greater than.where('age', '>', 18)
>=Greater than or equal.where('age', '>=', 18)
<Less than.where('price', '<', 100)
<=Less than or equal.where('price', '<=', 50)
inValue in array.where('status', 'in', ['active', 'pending'])
containsArray contains value.where('tags', 'contains', 'featured')

Subcollections

# Access a subcollectionposts_ref=scs.database \
.collection('users') \
.doc('user_id') \
.collection('posts')
# Add to subcollectionpost=posts_ref.add({
'title': 'My First Post',
'content': 'Hello World!',
'created_at': datetime.now().isoformat()
})
# Query subcollectionuser_posts=posts_ref \
.order_by('created_at', 'desc') \
.limit(5) \
.get()
# Nested subcollections (e.g., users/user_id/posts/post_id/comments)comments_ref=scs.database \
.collection('users') \
.doc('user_id') \
.collection('posts') \
.doc('post_id') \
.collection('comments')

Storage

# Upload from file pathfile_info=scs.storage.upload('./image.png')
# Upload with folderfile_info=scs.storage.upload('./doc.pdf', folder='documents')
# Upload from bytesfile_info=scs.storage.upload_bytes(
data=image_bytes,
filename='image.png',
content_type='image/png'
)
# List filesfiles=scs.storage.list_files(folder='images')
# Get file referencefile_ref=scs.storage.ref('file-id')
# Download filecontent=file_ref.download()
# Download to filefile_ref.download_to_file('./local_file.png')
# Get metadatametadata=file_ref.get_metadata()
# Delete filefile_ref.delete()
# Folder operationsscs.storage.create_folder('my-folder')
scs.storage.delete_folder('my-folder')

Realtime Database

Real-time data synchronization via WebSocket:

# Connect to realtime servicescs.realtime.connect()
# Get referencechat_ref=scs.realtime.ref('chat/room1')
# Subscribe to updatesdefon_message(data, event):
print(f'Event: {event}, Data: {data}')
unsubscribe=chat_ref.on(on_message)
# Subscribe to single updatechat_ref.once(lambdadata, event: print(data))
# Unsubscribeunsubscribe()
# Disconnectscs.realtime.disconnect()

Cloud Messaging

Push notifications with topics and direct messaging:

# Register device tokenscs.messaging.register_token(
token='device-fcm-token',
platform='android'
)
# Topic operationsscs.messaging.create_topic('news', description='News updates')
scs.messaging.subscribe_to_topic('news', 'device-token')
scs.messaging.unsubscribe_from_topic('news', 'device-token')
# Send to topicscs.messaging.send_to_topic(
topic='news',
title='Breaking News',
body='Something happened!',
data={'articleId': '123'}
)
# Send to specific devicescs.messaging.send_to_token(
token='device-token',
title='Personal Alert',
body='You have a new message'
)
# Send to multiple devicesscs.messaging.send_to_tokens(
tokens=['token1', 'token2'],
title='Broadcast',
body='Hello everyone!'
)

Remote Config

Dynamic app configuration:

# Fetch configurationconfig=scs.remote_config.fetch()
# Get values with type safetytheme=scs.remote_config.get_string('app_theme', default='light')
max_items=scs.remote_config.get_int('max_items', default=10)
enabled=scs.remote_config.get_bool('feature_enabled', default=False)
settings=scs.remote_config.get_json('settings', default={})
# Admin: Manage parametersscs.remote_config.create_param(
key='app_theme',
value='dark',
description='Default app theme'
)
scs.remote_config.update_param('app_theme', 'light')
scs.remote_config.delete_param('old_param')
# Publish changesscs.remote_config.publish()
# Version managementversions=scs.remote_config.list_versions()
scs.remote_config.rollback('version-id')

Serverless Functions

Execute custom code on the backend:

# Invoke a functionresult=scs.functions.invoke('processOrder', {
'orderId': '12345',
'action': 'confirm'
})
# Admin: Create a functionscs.functions.create(
name='processOrder',
code=''' module.exports = async (data, context) => { const { orderId, action } = data; // Process order... return { success: true, orderId }; }; ''',
runtime='nodejs18',
timeout=30000,
memory=256
)
# Admin: List functionsfunctions=scs.functions.list()
# Admin: Update functionscs.functions.update('function-id', code='...')
# Admin: Test functionresult=scs.functions.test('function-id', {'test': 'data'})
# Admin: Get logslogs=scs.functions.get_logs('function-id')

AI

Chat, text completion, and image generation with local LLM models:

# Chat with AIresponse=scs.ai.chat(
message="What is the capital of France?",
system_prompt="You are a helpful geography assistant."
)
print(response['content'])
# Text completionresponse=scs.ai.complete(prompt="Once upon a time")
print(response['content'])
# Generate imageresponse=scs.ai.generate_image(prompt="A sunset over mountains")
print(response['imageUrl'])
# List available modelsmodels=scs.ai.list_models()
# Conversation managementconversation=scs.ai.create_conversation(title="Geography Chat")
scs.ai.get_conversation(conversation['conversationId'])
scs.ai.delete_conversation(conversation['conversationId'])

AI Agents

Create and manage AI agents with custom instructions and tools:

# Create an agentagent=scs.ai.create_agent(
name="Customer Support",
instructions="You are a helpful customer support assistant. Be polite and helpful.",
model="llama3.2",
temperature=0.7
)
print(f"Created agent: {agent['agentId']}")
# List agentsagents=scs.ai.list_agents()
# Run the agentresponse=scs.ai.run_agent(
agent_id=agent['agentId'],
input="How do I reset my password?"
)
print(f"Agent: {response['output']}")
print(f"Session: {response['sessionId']}")
# Continue the conversation in the same sessionresponse=scs.ai.run_agent(
agent_id=agent['agentId'],
input="Thanks! What about enabling 2FA?",
session_id=response['sessionId']
)
# List agent sessionssessions=scs.ai.list_agent_sessions(agent['agentId'])
# Get full session historysession=scs.ai.get_agent_session(agent['agentId'], response['sessionId'])
formsginsession['messages']:
print(f"{msg['role']}: {msg['content']}")
# Update agentscs.ai.update_agent(
agent_id=agent['agentId'],
instructions="Updated instructions here",
temperature=0.5
)
# Define a tool for agentstool=scs.ai.define_tool(
name="get_weather",
description="Get weather for a location",
parameters={
"type": "object",
"properties": {
"location": {"type": "string", "description": "City name"}
}
}
)
# List toolstools=scs.ai.list_tools()
# Delete agent and sessionsscs.ai.delete_agent_session(agent['agentId'], response['sessionId'])
scs.ai.delete_agent(agent['agentId'])

Configuration File

Create an scs-info.json file:

{
"sdk_config": {
"api_key": "pk_your_api_key",
"project_id": "your-project-id",
"base_url": "https://your-scs-instance.com"
}
}

Error Handling

fromscsimportSCS, SCSError, AuthenticationError, NotFoundError, ValidationErrortry:
user=scs.auth.login(email='user@example.com', password='wrong')
exceptAuthenticationErrorase:
print(f'Auth failed: {e.message}')
print(f'Status: {e.status}')
exceptNotFoundErrorase:
print(f'Not found: {e.message}')
exceptValidationErrorase:
print(f'Validation error: {e.message}')
exceptSCSErrorase:
print(f'SCS error: {e.message}')

Context Manager

withSCS({'api_key': '...', 'project_id': '...'}) asscs:
users=scs.database.collection('users').get()
# Connection automatically closed

Requirements

  • Python 3.8+
  • requests
  • python-socketio[client] (optional, for realtime functionality)

License

MIT License

About

The official Python SDK for Spyxpo Cloud Services

Resources

Code of conduct

Contributing

Security policy

Stars

2 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Latest commit

History

13 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

SCS Python SDK

Python SDK for SCS (Spyxpo Cloud Services)

Installation

pip install scs-sdk

For realtime functionality (WebSocket support):

pip install scs-sdk[realtime]

Quick Start

fromscsimportSCS# Initialize from config filescs=SCS.initialize_app('./scs-info.json')
# Or initialize with config dictscs=SCS({
'api_key': 'your-api-key',
'project_id': 'your-project-id',
'base_url': 'https://your-scs-instance.com'
})
# Authenticationuser=scs.auth.login(email='user@example.com', password='password123')
# Databaseusers=scs.database.collection('users').where('age', '>=', 18).limit(10).get()
# Storagescs.storage.upload('./image.png', folder='images')

Services

Authentication

# Register a new useruser=scs.auth.register(
email='user@example.com',
password='password123',
display_name='John Doe'
)
# Loginuser=scs.auth.login(email='user@example.com', password='password123')
# Get current userme=scs.auth.get_current_user()
# Update profilescs.auth.update_profile(display_name='Jane Doe')
# Change passwordscs.auth.change_password(
current_password='old123',
new_password='new456'
)
# Logoutscs.auth.logout()

OAuth and Social Sign-In

SCS supports multiple OAuth providers for seamless social authentication. Each provider follows a similar pattern but requires different credentials obtained from their respective SDKs.

Google Sign-In

Authenticate users with their Google account. Requires the Google Sign-In SDK on the client.

# Sign in with Googleuser=scs.auth.sign_in_with_google(
id_token='google-id-token', # Required: ID token from Google Sign-Inaccess_token='google-access-token'# Optional: Access token for additional scopes
)
print(f"User ID: {user['uid']}")
print(f"Email: {user['email']}")
print(f"Display Name: {user['displayName']}")
print(f"Photo URL: {user['photoURL']}")
print(f"Provider: {user['providerId']}") # 'google'

Parameters:

ParameterTypeRequiredDescription
id_tokenstrYesThe ID token obtained from Google Sign-In SDK
access_tokenstrNoAccess token for additional Google API scopes

Returns:dict with user data and token

Example with Google OAuth Library:

fromgoogle.oauth2importid_tokenfromgoogle.auth.transportimportrequests# Verify and get token on server sidedefverify_google_token(token):
try:
idinfo=id_token.verify_oauth2_token(
token, requests.Request(), GOOGLE_CLIENT_ID
)
returnidinfoexceptValueError:
returnNone# Sign in with the tokenuser=scs.auth.sign_in_with_google(id_token=google_token)
Facebook Sign-In

Authenticate users with their Facebook account.

# Sign in with Facebookuser=scs.auth.sign_in_with_facebook(
access_token='facebook-access-token'# Required: Access token from Facebook Login
)
print(f"User: {user['displayName']}")
print(f"Email: {user['email']}") # May be None if user didn't grant email permission

Parameters:

ParameterTypeRequiredDescription
access_tokenstrYesAccess token from Facebook Login SDK

Example with Facebook SDK:

# After getting access token from Facebook SDK on clientdeffacebook_login(access_token):
try:
user=scs.auth.sign_in_with_facebook(access_token=access_token)
returnuserexceptExceptionase:
print(f"Facebook login failed: {e}")
returnNone
Apple Sign-In

Authenticate users with their Apple ID. Ideal for iOS apps and required for apps with social login on the App Store.

# Sign in with Appleuser=scs.auth.sign_in_with_apple(
identity_token='apple-identity-token', # Required: Identity token from Sign in with Appleauthorization_code='apple-auth-code', # Optional: Authorization code for server verificationfull_name='John Doe'# Optional: User's name (only available on first sign-in)
)

Parameters:

ParameterTypeRequiredDescription
identity_tokenstrYesJWT identity token from Sign in with Apple
authorization_codestrNoAuthorization code for additional verification
full_namestrNoUser's full name (Apple only provides this on first sign-in)

Important Notes:

  • Apple only provides the user's name on the first sign-in. Store it immediately.
  • Users can choose to hide their email (Apple provides a relay email).
  • Required for apps using social login on iOS/macOS.
GitHub Sign-In

Authenticate users with their GitHub account. Popular for developer-focused applications.

# Sign in with GitHubuser=scs.auth.sign_in_with_github(
code='github-oauth-code', # Required: OAuth authorization coderedirect_uri='https://yourapp.com/callback'# Optional: Must match OAuth app settings
)
print(f"GitHub username: {user['displayName']}")
print(f"Email: {user['email']}")

Parameters:

ParameterTypeRequiredDescription
codestrYesOAuth authorization code from GitHub OAuth flow
redirect_uristrNoRedirect URI (must match your GitHub OAuth App settings)

OAuth Flow Example (Flask):

fromflaskimportFlask, redirect, requestimportrequestsapp=Flask(__name__)
GITHUB_CLIENT_ID='your-client-id'GITHUB_CLIENT_SECRET='your-client-secret'@app.route('/login/github')defgithub_login():
returnredirect(
f'https://github.com/login/oauth/authorize'f'?client_id={GITHUB_CLIENT_ID}'f'&redirect_uri=https://yourapp.com/callback'f'&scope=read:user user:email'
)
@app.route('/callback')defgithub_callback():
code=request.args.get('code')
ifcode:
user=scs.auth.sign_in_with_github(
code=code,
redirect_uri='https://yourapp.com/callback'
)
returnf"Welcome, {user['displayName']}!"return"Login failed"
Twitter/X Sign-In

Authenticate users with their Twitter/X account using OAuth 1.0a.

# Sign in with Twitter/Xuser=scs.auth.sign_in_with_twitter(
oauth_token='twitter-oauth-token', # Required: OAuth tokenoauth_token_secret='twitter-oauth-secret'# Required: OAuth token secret
)

Parameters:

ParameterTypeRequiredDescription
oauth_tokenstrYesOAuth token from Twitter authentication
oauth_token_secretstrYesOAuth token secret from Twitter authentication

Note: Twitter uses OAuth 1.0a which requires a more complex flow. Consider using a library like tweepy or python-twitter.

Microsoft Sign-In

Authenticate users with their Microsoft account (personal, work, or school accounts).

# Sign in with Microsoftuser=scs.auth.sign_in_with_microsoft(
access_token='microsoft-access-token', # Required: Access token from MSALid_token='microsoft-id-token'# Optional: ID token for additional claims
)

Parameters:

ParameterTypeRequiredDescription
access_tokenstrYesAccess token from Microsoft Authentication Library (MSAL)
id_tokenstrNoID token for additional user claims

Example with MSAL Python:

frommsalimportConfidentialClientApplicationapp=ConfidentialClientApplication(
client_id="your-client-id",
authority="https://login.microsoftonline.com/common",
client_credential="your-client-secret"
)
# After getting tokens from authorization code flowresult=app.acquire_token_by_authorization_code(
code,
scopes=["User.Read"],
redirect_uri="https://yourapp.com/callback"
)
if"access_token"inresult:
user=scs.auth.sign_in_with_microsoft(
access_token=result["access_token"],
id_token=result.get("id_token")
)

Anonymous Authentication

Allow users to use your app without creating an account. Anonymous accounts can later be upgraded to permanent accounts by linking a provider.

# Sign in anonymously (creates a temporary account)user=scs.auth.sign_in_anonymously(
custom_data={'referrer': 'landing-page', 'campaign': 'summer-sale'} # optional
)
print(f"Anonymous user ID: {user['uid']}")
print(f"Is anonymous: {user['isAnonymous']}") # True

Parameters:

ParameterTypeRequiredDescription
custom_datadictNoCustom data to store with the anonymous user for analytics

Use Cases:

  • Allow users to try your app before signing up
  • Guest checkout in e-commerce
  • Save user progress/preferences before account creation
  • A/B testing with user tracking

Converting Anonymous to Permanent Account:

# User decides to create a permanent account# Link their anonymous account to a providertry:
user=scs.auth.link_provider('google', {
'idToken': 'google-id-token'
})
print("Account upgraded! User data preserved.")
print(f"Is anonymous: {user['isAnonymous']}") # FalseexceptExceptionase:
if'credential-already-in-use'instr(e):
# This Google account is already linked to another userprint("This account is already registered. Please sign in instead.")
else:
raise

Phone Number Authentication

Two-step authentication flow using SMS verification codes.

# Step 1: Send verification code to phoneresult=scs.auth.send_phone_verification_code(
phone_number='+1234567890', # Required: E.164 formatrecaptcha_token='recaptcha-token'# Optional: For bot protection
)
verification_id=result['verificationId']
print(f"Verification ID: {verification_id}")
# Store this ID - you'll need it in step 2# Step 2: User enters the code they receiveduser=scs.auth.sign_in_with_phone_number(
verification_id=verification_id, # The ID from step 1code='123456'# 6-digit code from SMS
)
print(f"Phone verified: {user['phoneNumber']}")

send_phone_verification_code Parameters:

ParameterTypeRequiredDescription
phone_numberstrYesPhone number in E.164 format (e.g., +1234567890)
recaptcha_tokenstrNoreCAPTCHA token for abuse prevention

sign_in_with_phone_number Parameters:

ParameterTypeRequiredDescription
verification_idstrYesVerification ID from send_phone_verification_code
codestrYes6-digit verification code from SMS

Complete Flow Example:

defsign_in_with_phone(phone_number: str, get_code_callback) ->dict:
""" Sign in with phone number. Args: phone_number: Phone number in E.164 format get_code_callback: Function that prompts user for the SMS code Returns: User dict on success """try:
# Step 1: Send coderesult=scs.auth.send_phone_verification_code(
phone_number=phone_number
)
verification_id=result['verificationId']
# Get code from user (your UI implementation)code=get_code_callback()
# Step 2: Verify codeuser=scs.auth.sign_in_with_phone_number(
verification_id=verification_id,
code=code
)
returnuserexceptExceptionase:
error_msg=str(e).lower()
if'invalid-phone-number'inerror_msg:
raiseValueError('Invalid phone number format. Use E.164 format (+1234567890)')
elif'too-many-requests'inerror_msg:
raiseValueError('Too many attempts. Please try again later.')
elif'invalid-verification-code'inerror_msg:
raiseValueError('Invalid verification code. Please try again.')
elif'code-expired'inerror_msg:
raiseValueError('Code expired. Please request a new one.')
else:
raise

Custom Token Authentication

Sign in using a JWT token generated by your own backend. Useful for migrating users from another system or integrating with custom authentication.

# Sign in with a custom token (generated by your backend)user=scs.auth.sign_in_with_custom_token('your-custom-jwt-token')
print(f"Signed in user: {user['uid']}")

Parameters:

ParameterTypeRequiredDescription
tokenstrYesJWT token generated by your backend

Backend Token Generation Example:

importjwtimporttimeimportosdefcreate_custom_token(uid: str, claims: dict=None) ->str:
""" Create a custom authentication token. Args: uid: Unique user identifier claims: Optional custom claims to include Returns: JWT token string """payload= {
'uid': uid,
'claims': claimsor {},
'iat': int(time.time()),
'exp': int(time.time()) +3600# 1 hour
}
returnjwt.encode(
payload,
os.environ['SCS_SECRET_KEY'],
algorithm='HS256'
)
# Generate token for a usercustom_token=create_custom_token('user-123', {'role': 'admin'})
# Send this token to the client for sign-in

Use Cases:

  • Migrating users from another authentication system
  • Server-side user creation with immediate client sign-in
  • Integration with enterprise SSO systems (SAML, LDAP)
  • Machine-to-machine authentication

Account Linking

Link multiple authentication providers to a single account. Users can sign in with any linked provider.

# Link a provider to current accountuser=scs.auth.link_provider('facebook', {
'accessToken': 'facebook-access-token'
})
print(f"Linked providers: {user['providerData']}")
# [{'providerId': 'password'}, {'providerId': 'google'}, {'providerId': 'facebook'}]# Unlink a provider from current accountuser=scs.auth.unlink_provider('facebook')
print(f"Remaining providers: {user['providerData']}")
# Get available sign-in methods for an emailresult=scs.auth.fetch_sign_in_methods_for_email('user@example.com')
print(f"Available methods: {result['methods']}")
# ['password', 'google', 'facebook']

link_provider Parameters:

ParameterTypeRequiredDescription
providerstrYesProvider name: 'google', 'facebook', 'apple', 'github', 'twitter', 'microsoft'
credentialsdictYesProvider-specific credentials (tokens)

Supported Providers and Credentials:

ProviderRequired Credentials
google{'idToken': '...', 'accessToken': '...'} (accessToken optional)
facebook{'accessToken': '...'}
apple{'identityToken': '...', 'authorizationCode': '...', 'fullName': '...'}
github{'code': '...', 'redirectUri': '...'}
twitter{'oauthToken': '...', 'oauthTokenSecret': '...'}
microsoft{'accessToken': '...', 'idToken': '...'}

Complete Account Linking Flow:

defcan_link_provider(email: str, provider: str) ->bool:
"""Check if a provider can be linked to an account."""result=scs.auth.fetch_sign_in_methods_for_email(email)
methods=result.get('methods', [])
ifproviderinmethods:
raiseValueError(f"{provider} is already linked to this account")
returnTruedeflink_google_account(google_id_token: str) ->dict:
"""Link Google to the current user's account."""try:
# Verify user is signed incurrent_user=scs.auth.get_current_user()
ifnotcurrent_user:
raiseValueError('Must be signed in to link accounts')
# Link the provideruser=scs.auth.link_provider('google', {
'idToken': google_id_token
})
print('Successfully linked Google account')
returnuserexceptExceptionase:
error_msg=str(e).lower()
if'credential-already-in-use'inerror_msg:
print('This Google account is already linked to another user')
elif'provider-already-linked'inerror_msg:
print('Google is already linked to this account')
raise

Password Reset & Email Verification

Handle password recovery and email verification flows.

# Send password reset emailscs.auth.send_password_reset_email('user@example.com')
print("Password reset email sent")
# Confirm password reset (user clicks link in email, you extract the code)scs.auth.confirm_password_reset(
code='reset-code-from-email', # Code from the reset linknew_password='newSecurePassword123'# User's new password
)
print("Password successfully reset")
# Send email verification to current userscs.auth.send_email_verification()
print("Verification email sent")
# Verify email with code (user clicks link, you extract the code)scs.auth.verify_email('verification-code')
print("Email verified")

Error Handling:

defreset_password(email: str) ->dict:
"""Send password reset email with error handling."""try:
scs.auth.send_password_reset_email(email)
return {'success': True, 'message': 'Reset email sent'}
exceptExceptionase:
error_msg=str(e).lower()
if'user-not-found'inerror_msg:
# Don't reveal if user exists for securityreturn {'success': True, 'message': 'If this email exists, a reset link was sent'}
elif'too-many-requests'inerror_msg:
return {'success': False, 'message': 'Too many attempts. Please try later.'}
else:
raisedefconfirm_reset(code: str, new_password: str) ->dict:
"""Confirm password reset with validation."""# Validate password strengthiflen(new_password) <8:
raiseValueError('Password must be at least 8 characters')
try:
scs.auth.confirm_password_reset(code=code, new_password=new_password)
return {'success': True}
exceptExceptionase:
error_msg=str(e).lower()
if'expired-action-code'inerror_msg:
return {'success': False, 'message': 'Reset link expired. Please request a new one.'}
elif'invalid-action-code'inerror_msg:
return {'success': False, 'message': 'Invalid reset link.'}
elif'weak-password'inerror_msg:
return {'success': False, 'message': 'Password is too weak.'}
else:
raise

Authentication State Management

Manage user sessions and authentication state.

# Check if user is logged inis_logged_in=scs.auth.is_logged_inprint(f"Is logged in: {is_logged_in}")
# Get current user (from cache)cached_user=scs.auth.current_user# Get current user (fresh from server)user=scs.auth.get_current_user()
# Refresh user datarefreshed_user=scs.auth.reload()
# Get the current auth tokentoken=scs.auth.token

Complete Authentication Example

fromscsimportSCS, AuthenticationErrorclassAuthService:
def__init__(self):
self.scs=SCS({
'api_key': 'your-api-key',
'project_id': 'your-project-id',
'base_url': 'https://your-scs-instance.com'
})
defregister(self, email: str, password: str, display_name: str) ->dict:
"""Register a new user with email verification."""user=self.scs.auth.register(
email=email,
password=password,
display_name=display_name
)
# Send verification emailself.scs.auth.send_email_verification()
returnuserdeflogin(self, email: str, password: str) ->dict:
"""Login with email and password."""returnself.scs.auth.login(email=email, password=password)
defsocial_login(self, provider: str, credentials: dict) ->dict:
"""Login with any social provider."""methods= {
'google': lambda: self.scs.auth.sign_in_with_google(**credentials),
'facebook': lambda: self.scs.auth.sign_in_with_facebook(**credentials),
'apple': lambda: self.scs.auth.sign_in_with_apple(**credentials),
'github': lambda: self.scs.auth.sign_in_with_github(**credentials),
'twitter': lambda: self.scs.auth.sign_in_with_twitter(**credentials),
'microsoft': lambda: self.scs.auth.sign_in_with_microsoft(**credentials)
}
ifprovidernotinmethods:
raiseValueError(f"Unknown provider: {provider}")
returnmethods[provider]()
defcontinue_as_guest(self, custom_data: dict=None) ->dict:
"""Sign in anonymously for guest access."""returnself.scs.auth.sign_in_anonymously(custom_data=custom_dataor {})
defupgrade_guest_account(self, provider: str, credentials: dict) ->dict:
"""Upgrade anonymous account to permanent."""user=self.scs.auth.get_current_user()
ifnotuserornotuser.get('isAnonymous'):
raiseValueError('Current user is not anonymous')
returnself.scs.auth.link_provider(provider, credentials)
deflogout(self):
"""Sign out the current user."""self.scs.auth.logout()
# Usageauth=AuthService()
# Register new useruser=auth.register('user@example.com', 'password123', 'John Doe')
# Or sign in with Googlegoogle_user=auth.social_login('google', {'id_token': 'xxx'})
# Or continue as guest and upgrade laterguest=auth.continue_as_guest({'source': 'homepage'})
# ... user decides to create account ...upgraded=auth.upgrade_guest_account('google', {'idToken': 'xxx'})

Database

Document database with query builder. SCS supports two powerful database options:

Database Types

TypeNameDescriptionBest For
eazieaZI DatabaseDocument-based NoSQL with Firestore-like collections, documents, and subcollectionsDevelopment, prototyping, small to medium apps
reladbRelaDBProduction-grade NoSQL database with relational-style viewsProduction, scalability, advanced queries

Initialize with eaZI (Default)

fromscsimportSCS# eaZI is the default database - no special configuration neededscs=SCS({
'project_id': 'your-project-id',
'api_key': 'your-api-key'# database_type: 'eazi' is implicit
})

Initialize with RelaDB (Production)

fromscsimportSCS# Use RelaDB for productionscs=SCS({
'project_id': 'your-project-id',
'api_key': 'your-api-key',
'database_type': 'reladb'# Enable RelaDB
})

eaZI Database Features

  • Document-based: Firestore-like collections and documents
  • Subcollections: Nested data organization
  • File-based storage: No external dependencies required
  • Zero configuration: Works out of the box
  • Query support: Filtering, ordering, and pagination

RelaDB Features

  • Production-ready: Built for reliability and performance
  • Scalable: Horizontal scaling and replication support
  • Advanced queries: Aggregation pipelines, complex filters
  • Indexing: Custom indexes for optimized performance
  • Schema flexibility: Dynamic schema with validation support
  • Relational-style views: Table view with columns and rows in the console

Collection Operations

# Get a collection referenceusers=scs.database.collection('users')
# List all collectionscollections=scs.database.list_collections()
# Create a collectionscs.database.create_collection('new_collection')
# Delete a collectionscs.database.delete_collection('old_collection')

Document Operations

# Add document with auto-generated IDdoc=scs.database.collection('users').add({
'name': 'John Doe',
'email': 'john@example.com',
'age': 30,
'tags': ['developer', 'python'],
'profile': {
'bio': 'Software developer',
'avatar': 'https://example.com/avatar.jpg'
}
})
print(f'Document ID: {doc["id"]}')
# Set document with custom ID (creates or overwrites)scs.database.collection('users').doc('user-123').set({
'name': 'Jane Doe',
'email': 'jane@example.com'
})
# Get a single documentuser=scs.database.collection('users').doc('user-123').get()
print(user)
# Update document (partial update)scs.database.collection('users').doc('user-123').update({
'age': 31,
'profile.bio': 'Senior developer'
})
# Delete documentscs.database.collection('users').doc('user-123').delete()

Query Operations

# Simple query with single filteractive_users=scs.database.collection('users') \
.where('status', '==', 'active') \
.get()
# Multiple filtersresults=scs.database.collection('users') \
.where('age', '>=', 18) \
.where('status', '==', 'active') \
.get()
# Ordering and paginationposts=scs.database.collection('posts') \
.where('published', '==', True) \
.order_by('created_at', 'desc') \
.limit(10) \
.skip(20) \
.get()
# Using 'in' operatorfeatured=scs.database.collection('posts') \
.where('category', 'in', ['tech', 'science', 'news']) \
.get()
# Using 'contains' for array fieldstagged=scs.database.collection('posts') \
.where('tags', 'contains', 'python') \
.get()

Query Operators

OperatorDescriptionExample
==Equal to.where('status', '==', 'active')
!=Not equal to.where('status', '!=', 'deleted')
>Greater than.where('age', '>', 18)
>=Greater than or equal.where('age', '>=', 18)
<Less than.where('price', '<', 100)
<=Less than or equal.where('price', '<=', 50)
inValue in array.where('status', 'in', ['active', 'pending'])
containsArray contains value.where('tags', 'contains', 'featured')

Subcollections

# Access a subcollectionposts_ref=scs.database \
.collection('users') \
.doc('user_id') \
.collection('posts')
# Add to subcollectionpost=posts_ref.add({
'title': 'My First Post',
'content': 'Hello World!',
'created_at': datetime.now().isoformat()
})
# Query subcollectionuser_posts=posts_ref \
.order_by('created_at', 'desc') \
.limit(5) \
.get()
# Nested subcollections (e.g., users/user_id/posts/post_id/comments)comments_ref=scs.database \
.collection('users') \
.doc('user_id') \
.collection('posts') \
.doc('post_id') \
.collection('comments')

Storage

# Upload from file pathfile_info=scs.storage.upload('./image.png')
# Upload with folderfile_info=scs.storage.upload('./doc.pdf', folder='documents')
# Upload from bytesfile_info=scs.storage.upload_bytes(
data=image_bytes,
filename='image.png',
content_type='image/png'
)
# List filesfiles=scs.storage.list_files(folder='images')
# Get file referencefile_ref=scs.storage.ref('file-id')
# Download filecontent=file_ref.download()
# Download to filefile_ref.download_to_file('./local_file.png')
# Get metadatametadata=file_ref.get_metadata()
# Delete filefile_ref.delete()
# Folder operationsscs.storage.create_folder('my-folder')
scs.storage.delete_folder('my-folder')

Realtime Database

Real-time data synchronization via WebSocket:

# Connect to realtime servicescs.realtime.connect()
# Get referencechat_ref=scs.realtime.ref('chat/room1')
# Subscribe to updatesdefon_message(data, event):
print(f'Event: {event}, Data: {data}')
unsubscribe=chat_ref.on(on_message)
# Subscribe to single updatechat_ref.once(lambdadata, event: print(data))
# Unsubscribeunsubscribe()
# Disconnectscs.realtime.disconnect()

Cloud Messaging

Push notifications with topics and direct messaging:

# Register device tokenscs.messaging.register_token(
token='device-fcm-token',
platform='android'
)
# Topic operationsscs.messaging.create_topic('news', description='News updates')
scs.messaging.subscribe_to_topic('news', 'device-token')
scs.messaging.unsubscribe_from_topic('news', 'device-token')
# Send to topicscs.messaging.send_to_topic(
topic='news',
title='Breaking News',
body='Something happened!',
data={'articleId': '123'}
)
# Send to specific devicescs.messaging.send_to_token(
token='device-token',
title='Personal Alert',
body='You have a new message'
)
# Send to multiple devicesscs.messaging.send_to_tokens(
tokens=['token1', 'token2'],
title='Broadcast',
body='Hello everyone!'
)

Remote Config

Dynamic app configuration:

# Fetch configurationconfig=scs.remote_config.fetch()
# Get values with type safetytheme=scs.remote_config.get_string('app_theme', default='light')
max_items=scs.remote_config.get_int('max_items', default=10)
enabled=scs.remote_config.get_bool('feature_enabled', default=False)
settings=scs.remote_config.get_json('settings', default={})
# Admin: Manage parametersscs.remote_config.create_param(
key='app_theme',
value='dark',
description='Default app theme'
)
scs.remote_config.update_param('app_theme', 'light')
scs.remote_config.delete_param('old_param')
# Publish changesscs.remote_config.publish()
# Version managementversions=scs.remote_config.list_versions()
scs.remote_config.rollback('version-id')

Serverless Functions

Execute custom code on the backend:

# Invoke a functionresult=scs.functions.invoke('processOrder', {
'orderId': '12345',
'action': 'confirm'
})
# Admin: Create a functionscs.functions.create(
name='processOrder',
code=''' module.exports = async (data, context) => { const { orderId, action } = data; // Process order... return { success: true, orderId }; }; ''',
runtime='nodejs18',
timeout=30000,
memory=256
)
# Admin: List functionsfunctions=scs.functions.list()
# Admin: Update functionscs.functions.update('function-id', code='...')
# Admin: Test functionresult=scs.functions.test('function-id', {'test': 'data'})
# Admin: Get logslogs=scs.functions.get_logs('function-id')

AI

Chat, text completion, and image generation with local LLM models:

# Chat with AIresponse=scs.ai.chat(
message="What is the capital of France?",
system_prompt="You are a helpful geography assistant."
)
print(response['content'])
# Text completionresponse=scs.ai.complete(prompt="Once upon a time")
print(response['content'])
# Generate imageresponse=scs.ai.generate_image(prompt="A sunset over mountains")
print(response['imageUrl'])
# List available modelsmodels=scs.ai.list_models()
# Conversation managementconversation=scs.ai.create_conversation(title="Geography Chat")
scs.ai.get_conversation(conversation['conversationId'])
scs.ai.delete_conversation(conversation['conversationId'])

AI Agents

Create and manage AI agents with custom instructions and tools:

# Create an agentagent=scs.ai.create_agent(
name="Customer Support",
instructions="You are a helpful customer support assistant. Be polite and helpful.",
model="llama3.2",
temperature=0.7
)
print(f"Created agent: {agent['agentId']}")
# List agentsagents=scs.ai.list_agents()
# Run the agentresponse=scs.ai.run_agent(
agent_id=agent['agentId'],
input="How do I reset my password?"
)
print(f"Agent: {response['output']}")
print(f"Session: {response['sessionId']}")
# Continue the conversation in the same sessionresponse=scs.ai.run_agent(
agent_id=agent['agentId'],
input="Thanks! What about enabling 2FA?",
session_id=response['sessionId']
)
# List agent sessionssessions=scs.ai.list_agent_sessions(agent['agentId'])
# Get full session historysession=scs.ai.get_agent_session(agent['agentId'], response['sessionId'])
formsginsession['messages']:
print(f"{msg['role']}: {msg['content']}")
# Update agentscs.ai.update_agent(
agent_id=agent['agentId'],
instructions="Updated instructions here",
temperature=0.5
)
# Define a tool for agentstool=scs.ai.define_tool(
name="get_weather",
description="Get weather for a location",
parameters={
"type": "object",
"properties": {
"location": {"type": "string", "description": "City name"}
}
}
)
# List toolstools=scs.ai.list_tools()
# Delete agent and sessionsscs.ai.delete_agent_session(agent['agentId'], response['sessionId'])
scs.ai.delete_agent(agent['agentId'])

Configuration File

Create an scs-info.json file:

{
"sdk_config": {
"api_key": "pk_your_api_key",
"project_id": "your-project-id",
"base_url": "https://your-scs-instance.com"
}
}

Error Handling

fromscsimportSCS, SCSError, AuthenticationError, NotFoundError, ValidationErrortry:
user=scs.auth.login(email='user@example.com', password='wrong')
exceptAuthenticationErrorase:
print(f'Auth failed: {e.message}')
print(f'Status: {e.status}')
exceptNotFoundErrorase:
print(f'Not found: {e.message}')
exceptValidationErrorase:
print(f'Validation error: {e.message}')
exceptSCSErrorase:
print(f'SCS error: {e.message}')

Context Manager

withSCS({'api_key': '...', 'project_id': '...'}) asscs:
users=scs.database.collection('users').get()
# Connection automatically closed

Requirements

  • Python 3.8+
  • requests
  • python-socketio[client] (optional, for realtime functionality)

License

MIT License

About

The official Python SDK for Spyxpo Cloud Services

Resources

Code of conduct

Contributing

Security policy

Stars

2 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages