Skip to main content

Python SDK

The Mobiscroll Connect Python SDK provides a convenient way to integrate Mobiscroll Connect in Python backend applications. It supports both synchronous and asynchronous usage and requires Python 3.9 or higher.

Setup​

Install the package using pip:

pip install mobiscroll-connect-sdk

Client Initialization​

To use the SDK, initialize MobiscrollConnectClient with your client credentials.

Class: mobiscroll_connect.MobiscrollConnectClient

constructorMobiscrollConnectClient

Constructor arguments.

client_idstr

Your Client ID obtained from the Mobiscroll Connect dashboard.

client_secretstr

Your Client Secret obtained from the Mobiscroll Connect dashboard.

redirect_uristr

Your application's redirect URI that matches the one configured in the Mobiscroll Connect dashboard.

Usage:

from mobiscroll_connect import MobiscrollConnectClient

client = MobiscrollConnectClient(
client_id="YOUR_CLIENT_ID",
client_secret="YOUR_CLIENT_SECRET",
redirect_uri="YOUR_REDIRECT_URI",
)

Use the client as a context manager to ensure the HTTP connection pool is released when done:

with MobiscrollConnectClient(
client_id="YOUR_CLIENT_ID",
client_secret="YOUR_CLIENT_SECRET",
redirect_uri="YOUR_REDIRECT_URI",
) as client:
calendars = client.calendars.list()

Methods​

set_credentials​

Sets the access token for the client. This is required before making any API calls that require authentication.

Method: client.set_credentials(tokens)

tokensTokenResponse

The token response object returned by client.auth.get_token(code).

on_tokens_refreshed​

Registers a callback to be invoked whenever the SDK automatically refreshes the access token. Use this to persist the updated tokens so they survive future requests.

Method: client.on_tokens_refreshed(callback)

callbackCallable[[TokenResponse], None]

A callable that receives the updated TokenResponse after a successful automatic token refresh. The async client also accepts an async callable.

Token Refresh​

The Python SDK handles token refresh automatically. When any API call returns a 401 Unauthorized response and the client has a refresh_token stored, the SDK will silently exchange it for a new access token and retry the original request — with no action required from your application.

When the refresh succeeds, the SDK invokes your on_tokens_refreshed callback with the updated TokenResponse. You must register this callback and persist the new tokens, otherwise they will be lost between requests.

from mobiscroll_connect import TokenResponse

def persist_tokens(tokens: TokenResponse) -> None:
# Persist tokens in your database or session store
# so the new access_token and refresh_token survive future requests
session["access_token"] = tokens.access_token
session["refresh_token"] = tokens.refresh_token

client.on_tokens_refreshed(persist_tokens)

If the refresh token itself is invalid or has been revoked, the SDK raises an AuthenticationError and the user must re-authorize.

Async Usage​

The SDK ships an async client with an identical API surface. Import it from the aio subpackage and use async with to manage the connection pool:

from mobiscroll_connect.aio import AsyncMobiscrollConnectClient

async with AsyncMobiscrollConnectClient(
client_id="YOUR_CLIENT_ID",
client_secret="YOUR_CLIENT_SECRET",
redirect_uri="YOUR_REDIRECT_URI",
) as client:
client.auth.set_credentials(tokens)
calendars = await client.calendars.list()

All resource methods on the async client are coroutines. The on_tokens_refreshed callback may be sync or async — both are supported.

Error Handling​

All SDK methods raise exceptions that extend MobiscrollConnectError. You can catch the base class or any specific subclass.

ExceptionHTTP Statuserror.code
AuthenticationError401, 403AUTHENTICATION_ERROR
CalendarPermissionError403CALENDAR_PERMISSION_REQUIRED
ValidationError400, 422VALIDATION_ERROR
NotFoundError404NOT_FOUND_ERROR
RateLimitError429RATE_LIMIT_ERROR
ServerError5xxSERVER_ERROR
NetworkError—NETWORK_ERROR

ValidationError exposes a details property with field-level validation errors. RateLimitError exposes retry_after (seconds) and ServerError exposes status_code.

from mobiscroll_connect.exceptions import (
AuthenticationError,
ValidationError,
RateLimitError,
MobiscrollConnectError,
)

try:
response = client.events.list(start="2024-01-01")
except AuthenticationError:
# Token expired and refresh failed — re-authorize the user
pass
except ValidationError as e:
print(e.details)
except RateLimitError as e:
print(f"Retry after {e.retry_after}s")
except MobiscrollConnectError as e:
# Catch-all for any other SDK error
pass

Auth API​

The client.auth resource handles the OAuth authorization flow, including generating authorization URLs, exchanging codes for tokens, managing connection status, and disconnecting providers.

Each account returned by get_connection_status reports granted_scopes and calendar_permission_granted. A false flag means the account connected but withheld calendar access on the provider's consent screen, so it can list no calendars until the user reconnects — see Partial consent. Each account also reports syncState: reauth_required means the provider has since rejected the stored credentials and the account has stopped syncing — a different question from calendarPermissionGranted, which only records what was agreed at connect time. See Connection health.

When no connected account has calendar access, calendar and event calls raise CalendarPermissionError — accounts names the accounts that must reconnect. It subclasses AuthenticationError, so existing handlers keep working.

To localize the Connect pages, pass an optional lng to generate_auth_url, e.g. generate_auth_url(user_id=..., lng='es'). When omitted, the UI falls back to the browser's Accept-Language header, then English; Arabic, Hebrew and Persian render right-to-left. See Supported languages for the languages Connect supports.


auth.generate_auth_url​

Builds the URL that starts the authorization flow. Redirect the user to it: once they connect their calendars, Connect redirects them back to your redirect URI with an authorization code. The URL is built locally, without an API request. See GET /authorize.

Method: client.auth.generate_auth_url(user_id, *, scope, state, lng)

Returns: str

user_idstr

Your identifier for the user. scope, state and lng are optional keyword arguments.

Usage:

# Generate the authorization URL
auth_url = client.auth.generate_auth_url(
user_id='user-456',
# Optional parameters:
# scope='read-write',
# state='xyz789',
# lng='es', # localize the Connect pages
)

# Redirect the user to auth_url
# return redirect(auth_url)

auth.get_token​

Exchanges the authorization code from your redirect URI for an access token and a refresh token. The client stores the returned tokens and uses them for the calls that follow. See POST /token.

Method: client.auth.get_token(code)

Returns: TokenResponse

codestr

The authorization code received on your redirect URI.

Usage:

# Exchange authorization code for access token
# The client is automatically authenticated after get_token()
token_response = client.auth.get_token(code)

# Persist tokens for future requests
session['access_token'] = token_response.access_token
session['refresh_token'] = token_response.refresh_token

Calendars API​

The client.calendars resource allows you to list available calendars from all connected providers (Google, Outlook, etc.). It corresponds to the /calendars endpoints.


calendars.list​

Lists the calendars of every account the user has connected. See GET /calendars.

Method: client.calendars.list()

Returns: list[Calendar]

Usage:

calendars = client.calendars.list()

Events API​

The client.events resource provides methods to create, read, update, and delete calendar events across all connected accounts. It corresponds to the /events endpoints.


events.list​

Lists events from the user's calendars, one page at a time. When the response contains a next_page_token, pass it to the next call to get the following page. See GET /events.

Method: client.events.list(*, start=None, end=None, calendar_ids=None, page_size=None, next_page_token=None, single_events=None)

Returns: EventsListResponse

Calendar IDs grouped by provider, for example {'google': ['work@company.com']}. All arguments are optional and keyword-only.

Usage:

# Fetch initial events with date range filter
response = client.events.list(
page_size=50,
start='2025-10-01T00:00:00Z',
end='2025-10-31T23:59:59Z',
)

# Load more events using next_page_token
next_response = client.events.list(
page_size=50,
next_page_token=response.next_page_token,
)

# Filter by specific calendars
filtered_events = client.events.list(
page_size=25,
calendar_ids={'google': ['personal@gmail.com', 'work@company.com']},
)

# Get recurring event series masters (not expanded)
masters = client.events.list(
page_size=50,
single_events=False,
)

events.create​

Creates an event in the given calendar. Set recurrence to create a recurring series. See POST /event.

Method: client.events.create(event)

Returns: CalendarEvent

A dict with the event fields, using the API field names. provider, calendarId, title, start and end are required. Optional fields include description, location, allDay and recurrence.

Usage:

# Create a simple event
event = client.events.create({
'provider': 'google',
'calendarId': 'primary',
'title': 'Team Meeting',
'description': 'Discuss project updates',
'start': '2025-11-01T10:00:00Z',
'end': '2025-11-01T11:00:00Z',
'location': 'Conference Room A',
})

# Create a recurring event
recurring_event = client.events.create({
'provider': 'microsoft',
'calendarId': 'AAMkAGVmMDEz...',
'title': 'Weekly Standup',
'start': '2025-11-01T09:00:00Z',
'end': '2025-11-01T09:30:00Z',
'allDay': False,
'recurrence': {
'frequency': 'WEEKLY',
'interval': 1,
'count': 10,
'byDay': ['MO', 'WE', 'FR'],
},
})

# Create an all-day event
all_day_event = client.events.create({
'provider': 'apple',
'calendarId': 'https://caldav.icloud.com/.../calendars/...',
'title': 'Conference',
'start': '2025-11-15T00:00:00Z',
'end': '2025-11-16T00:00:00Z',
'allDay': True,
'description': 'Annual tech conference',
})

events.update​

Updates an existing event. For an occurrence of a recurring series, set recurringEventId, and use updateMode to choose which occurrences change: this, following or all. See PUT /event.

Method: client.events.update(event)

Returns: CalendarEvent

A dict with provider, calendarId and eventId, plus the fields you want to change. Add recurringEventId and updateMode for recurring events.

Usage:

# Update a simple event
updated_event = client.events.update({
'provider': 'google',
'calendarId': 'primary',
'eventId': 'event123abc',
'title': 'Updated Team Meeting',
'start': '2025-11-01T14:00:00Z',
'end': '2025-11-01T15:00:00Z',
})

# Update a single instance of a recurring event
updated_instance = client.events.update({
'provider': 'microsoft',
'eventId': 'instance456',
'recurringEventId': 'series123',
'calendarId': 'AAMkAGVmMDEz...',
'updateMode': 'this',
'title': 'Standup - Special Topic Today',
'start': '2025-11-01T09:00:00Z',
'end': '2025-11-01T10:00:00Z',
})

# Update all future occurrences
updated_future = client.events.update({
'provider': 'apple',
'eventId': 'recurring-event-id',
'calendarId': 'https://caldav.icloud.com/.../calendars/...',
'updateMode': 'following',
'location': 'New Conference Room B',
})

events.delete​

Deletes an event. For an occurrence of a recurring series, set recurringEventId, and use deleteMode to choose which occurrences are deleted: this, following or all. See DELETE /event.

Method: client.events.delete(params)

Returns: None

A dict with provider, calendarId and eventId. recurringEventId and deleteMode are optional.

Usage:

# Delete a simple event
client.events.delete({
'provider': 'google',
'calendarId': 'primary',
'eventId': 'event123abc',
})

# Delete a single instance of a recurring event
client.events.delete({
'provider': 'microsoft',
'calendarId': 'AAMkAGVmMDEz...',
'eventId': 'instance456',
'recurringEventId': 'series123',
'deleteMode': 'this',
})

# Delete entire recurring series
client.events.delete({
'provider': 'apple',
'calendarId': 'https://caldav.icloud.com/.../calendars/...',
'eventId': 'recurring-event-id',
'deleteMode': 'all',
})

Webhooks API​

The client.webhooks resource lets you subscribe a calendar to change notifications and unsubscribe an existing channel. It corresponds to the /subscribe-webhook and /unsubscribe-webhook endpoints. Connect subscribes and renews channels automatically when a calendar is connected — this resource is for managing a specific channel explicitly.


webhooks.subscribe_webhook​

Subscribes a calendar to change notifications. See POST /subscribe-webhook.

Method: client.webhooks.subscribe_webhook(provider, calendar_id, *, channel_id=None, expiration=None)

Returns: SubscribeWebhookResponse

providerstr

The calendar's provider: google, microsoft, apple or caldav.

calendar_idstr

The calendar to subscribe. channel_id is optional and generated by the server when omitted. expiration is an optional Unix timestamp in milliseconds.

Usage:

subscription = client.webhooks.subscribe_webhook(
'google',
'work@company.com',
channel_id='my-channel-123'
)

webhooks.unsubscribe_webhook​

Removes an existing webhook subscription. See POST /unsubscribe-webhook.

Method: client.webhooks.unsubscribe_webhook(provider, channel_id, *, resource_id=None)

Returns: UnsubscribeWebhookResponse

providerstr

The calendar's provider: google, microsoft, apple or caldav.

channel_idstr

The channel to remove. resource_id is the provider resource ID returned by subscribe_webhook, where the provider uses one.

Usage:

client.webhooks.unsubscribe_webhook(
'google',
'my-channel-123',
resource_id='resource-abc'
)