Java SDK
The Mobiscroll Connect Java SDK provides a convenient way to integrate Mobiscroll Connect in Java backend applications. It targets Java 11 and is built on top of OkHttp 4 and Jackson, so it works in any modern JVM application (plain Java, Spring Boot, Quarkus, etc.).
Setup
Add the dependency using your build tool of choice:
- Maven
- Gradle (Kotlin)
- Gradle (Groovy)
<dependency>
<groupId>com.mobiscroll</groupId>
<artifactId>connect-sdk</artifactId>
<version>1.1.0</version>
</dependency>
implementation("com.mobiscroll:connect-sdk:1.0.0")
implementation 'com.mobiscroll:connect-sdk:1.0.0'
Client Initialization
To use the SDK, initialize MobiscrollConnectClient with your client credentials.
Class: com.mobiscroll.connect.MobiscrollConnectClient
Constructor arguments.
Your Client ID obtained from the Mobiscroll Connect dashboard.
Your Client Secret obtained from the Mobiscroll Connect dashboard.
Your application's redirect URI that matches the one configured in the Mobiscroll Connect dashboard.
Usage:
import com.mobiscroll.connect.MobiscrollConnectClient;
MobiscrollConnectClient client = new MobiscrollConnectClient(
"YOUR_CLIENT_ID",
"YOUR_CLIENT_SECRET",
"YOUR_REDIRECT_URI"
);
For a custom base URL, HTTP timeout, OkHttp client, or a token-refresh callback for persistence, use the configuration builder:
import com.mobiscroll.connect.MobiscrollConnectClient;
import com.mobiscroll.connect.MobiscrollConnectConfig;
import java.time.Duration;
MobiscrollConnectClient client = new MobiscrollConnectClient(
MobiscrollConnectConfig.builder()
.clientId("YOUR_CLIENT_ID")
.clientSecret("YOUR_CLIENT_SECRET")
.redirectUri("YOUR_REDIRECT_URI")
.baseUrl("https://connect.mobiscroll.com/api")
.timeout(Duration.ofSeconds(60))
.onTokensRefreshed(updatedTokens -> {
// Persist updatedTokens.getAccessToken() / getRefreshToken()
})
.build()
);
Methods
setCredentials
Sets the access token for the client. This is required before making any API calls that require authentication.
Method: client.setCredentials(tokens)
The token response object returned by client.auth().getToken(code).
onTokensRefreshed
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.onTokensRefreshed(callback)
A consumer that receives the updated TokenResponse after a successful automatic token refresh.
Token Refresh
The Java SDK handles token refresh automatically. When any API call returns a 401 Unauthorized response and the client has a refresh_token stored, the SDK silently exchanges it for a new access token and retries the original request — with no action required from your application. Concurrent calls that hit the same expired token share a single refresh attempt.
When the refresh succeeds, the SDK invokes your onTokensRefreshed callback with the updated TokenResponse. You must register this callback and persist the new tokens, otherwise they will be lost between requests.
client.onTokensRefreshed(updatedTokens -> {
// Persist updatedTokens in your database or session store
// so the new access_token and refresh_token survive future requests
httpSession.setAttribute("access_token", updatedTokens.getAccessToken());
httpSession.setAttribute("refresh_token", updatedTokens.getRefreshToken());
});
If the refresh token itself is invalid or has been revoked, the SDK throws an AuthenticationException and the user must re-authorize.
Error Handling
All SDK methods throw exceptions that extend com.mobiscroll.connect.exceptions.MobiscrollConnectException. You can catch the base class or any specific subclass.
| Exception | HTTP Status | Extra |
|---|---|---|
AuthenticationException | 401, 403 | — (raised after refresh + retry has been exhausted) |
CalendarPermissionException | 403 | getAccounts() — the accounts that must reconnect |
ValidationException | 400, 422 | getDetails() (JsonNode) |
NotFoundException | 404 | — |
RateLimitException | 429 | getRetryAfter() (Integer, seconds) |
ServerException | 5xx | getStatusCode() (int) |
NetworkException | — | wraps the underlying IOException cause |
import com.mobiscroll.connect.exceptions.*;
import com.mobiscroll.connect.models.EventListParams;
import java.time.OffsetDateTime;
try {
var response = client.events().list(EventListParams.builder()
.start(OffsetDateTime.parse("2026-01-01T00:00:00Z"))
.end(OffsetDateTime.parse("2026-02-01T00:00:00Z"))
.build());
} catch (AuthenticationException e) {
// Token expired and refresh failed — re-authorize the user
} catch (ValidationException e) {
// Invalid request parameters
var details = e.getDetails();
} catch (RateLimitException e) {
Integer retryAfter = e.getRetryAfter(); // seconds
} catch (MobiscrollConnectException e) {
// Catch-all for any other SDK error
}
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 getConnectionStatus reports getGrantedScopes() and getCalendarPermissionGranted(). 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 CalendarPermissionException — getAccounts() names the accounts that must reconnect. It subclasses AuthenticationException, so existing handlers keep working.
To localize the Connect pages, pass an optional lng to generateAuthUrl, e.g. AuthUrlParams.builder().userId(...).lng("es").build(). 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.
🔐 OAuth API Reference
auth().generateAuthUrl
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().generateAuthUrl(AuthUrlParams params)
Returns: String
Built with AuthUrlParams.builder(). userId is required: your identifier for the user. scope, state and lng are optional.
Usage:
import com.mobiscroll.connect.models.AuthUrlParams;
// Generate the authorization URL
String authUrl = client.auth().generateAuthUrl(AuthUrlParams.builder()
.userId("user-456")
// Optional parameters:
// .scope("read-write")
// .state("xyz789")
// .lng("es") // localize the Connect pages
.build());
// Redirect the user to authUrl
// response.sendRedirect(authUrl);
auth().getToken
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().getToken(String code)
Returns: TokenResponse
The authorization code received on your redirect URI.
Usage:
import com.mobiscroll.connect.models.TokenResponse;
// Exchange authorization code for access token
// The client is automatically authenticated with the new token
TokenResponse tokens = client.auth().getToken(code);
// Persist tokens server-side keyed by your user
httpSession.setAttribute("access_token", tokens.getAccessToken());
httpSession.setAttribute("refresh_token", tokens.getRefreshToken());
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 API Reference
calendars().list
Lists the calendars of every account the user has connected. See GET /calendars.
Method: client.calendars().list()
Returns: List<Calendar>
Usage:
import com.mobiscroll.connect.models.Calendar;
import java.util.List;
List<Calendar> 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 API Reference
events().list
Lists events from the user's calendars, one page at a time. When the response contains a nextPageToken, pass it to the next call to get the following page. See GET /events.
Method: client.events().list(EventListParams params)
Returns: EventsListResponse
Built with EventListParams.builder(). All fields are optional: start, end, calendarIds (calendar IDs grouped by Provider), pageSize, nextPageToken and singleEvents. Can be null.
Usage:
import com.mobiscroll.connect.Provider;
import com.mobiscroll.connect.models.EventListParams;
import com.mobiscroll.connect.models.EventsListResponse;
import java.time.OffsetDateTime;
import java.util.List;
import java.util.Map;
// Fetch initial events with date range filter
EventsListResponse response = client.events().list(EventListParams.builder()
.pageSize(50)
.start(OffsetDateTime.parse("2025-10-01T00:00:00Z"))
.end(OffsetDateTime.parse("2025-10-31T23:59:59Z"))
.build());
// Load more events using nextPageToken
EventsListResponse nextResponse = client.events().list(EventListParams.builder()
.pageSize(50)
.nextPageToken(response.getNextPageToken())
.build());
// Filter by specific calendars
EventsListResponse filtered = client.events().list(EventListParams.builder()
.pageSize(25)
.calendarIds(Map.of(
Provider.GOOGLE, List.of("personal@gmail.com", "work@company.com")
))
.build());
// Get recurring event series masters (not expanded)
EventsListResponse masters = client.events().list(EventListParams.builder()
.pageSize(50)
.singleEvents(false)
.build());
events().create
Creates an event in the given calendar. Set recurrence to create a recurring series. See POST /event.
Method: client.events().create(EventCreateData data)
Returns: CalendarEvent
Built with EventCreateData.builder(). provider, calendarId, title, start and end are required. Optional fields include description, location, allDay and recurrence.
Usage:
import com.mobiscroll.connect.Provider;
import com.mobiscroll.connect.models.CalendarEvent;
import com.mobiscroll.connect.models.EventCreateData;
import com.mobiscroll.connect.models.RecurrenceRule;
import java.time.OffsetDateTime;
import java.util.List;
// Create a simple event
CalendarEvent event = client.events().create(EventCreateData.builder()
.provider(Provider.GOOGLE)
.calendarId("primary")
.title("Team Meeting")
.description("Discuss project updates")
.start(OffsetDateTime.parse("2025-11-01T10:00:00Z"))
.end(OffsetDateTime.parse("2025-11-01T11:00:00Z"))
.location("Conference Room A")
.build());
// Create a recurring event
CalendarEvent recurringEvent = client.events().create(EventCreateData.builder()
.provider(Provider.MICROSOFT)
.calendarId("AAMkAGVmMDEz...")
.title("Weekly Standup")
.start(OffsetDateTime.parse("2025-11-01T09:00:00Z"))
.end(OffsetDateTime.parse("2025-11-01T09:30:00Z"))
.allDay(false)
.recurrence(RecurrenceRule.builder()
.frequency("WEEKLY")
.interval(1)
.count(10)
.byDay(List.of("MO", "WE", "FR"))
.build())
.build());
// Create an all-day event
CalendarEvent allDayEvent = client.events().create(EventCreateData.builder()
.provider(Provider.APPLE)
.calendarId("https://caldav.icloud.com/.../calendars/...")
.title("Conference")
.start(OffsetDateTime.parse("2025-11-15T00:00:00Z"))
.end(OffsetDateTime.parse("2025-11-16T00:00:00Z"))
.allDay(true)
.description("Annual tech conference")
.build());
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(EventUpdateData data)
Returns: CalendarEvent
Built with EventUpdateData.builder(). provider, calendarId and eventId are required. Set only the fields you want to change, plus recurringEventId and updateMode for recurring events.
Usage:
import com.mobiscroll.connect.Provider;
import com.mobiscroll.connect.models.CalendarEvent;
import com.mobiscroll.connect.models.EventUpdateData;
import java.time.OffsetDateTime;
// Update a simple event
CalendarEvent updatedEvent = client.events().update(EventUpdateData.builder()
.provider(Provider.GOOGLE)
.calendarId("primary")
.eventId("event123abc")
.title("Updated Team Meeting")
.start(OffsetDateTime.parse("2025-11-01T14:00:00Z"))
.end(OffsetDateTime.parse("2025-11-01T15:00:00Z"))
.build());
// Update a single instance of a recurring event
CalendarEvent updatedInstance = client.events().update(EventUpdateData.builder()
.provider(Provider.MICROSOFT)
.eventId("instance456")
.recurringEventId("series123")
.calendarId("AAMkAGVmMDEz...")
.updateMode("this")
.title("Standup - Special Topic Today")
.start(OffsetDateTime.parse("2025-11-01T09:00:00Z"))
.end(OffsetDateTime.parse("2025-11-01T10:00:00Z"))
.build());
// Update all future occurrences
CalendarEvent updatedFuture = client.events().update(EventUpdateData.builder()
.provider(Provider.APPLE)
.eventId("recurring-event-id")
.calendarId("https://caldav.icloud.com/.../calendars/...")
.updateMode("following")
.location("New Conference Room B")
.build());
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(EventDeleteParams params)
Returns: void
Built with EventDeleteParams.builder(). provider, calendarId and eventId are required. recurringEventId and deleteMode are optional.
Usage:
import com.mobiscroll.connect.Provider;
import com.mobiscroll.connect.models.EventDeleteParams;
// Delete a simple event
client.events().delete(EventDeleteParams.builder()
.provider(Provider.GOOGLE)
.calendarId("primary")
.eventId("event123abc")
.build());
// Delete a single instance of a recurring event
client.events().delete(EventDeleteParams.builder()
.provider(Provider.MICROSOFT)
.calendarId("AAMkAGVmMDEz...")
.eventId("instance456")
.recurringEventId("series123")
.deleteMode("this")
.build());
// Delete entire recurring series
client.events().delete(EventDeleteParams.builder()
.provider(Provider.APPLE)
.calendarId("https://caldav.icloud.com/.../calendars/...")
.eventId("recurring-event-id")
.deleteMode("all")
.build());
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 API Reference
webhooks().subscribeWebhook
Subscribes a calendar to change notifications. See POST /subscribe-webhook.
Method: client.webhooks().subscribeWebhook(WebhookSubscribeParams params)
Returns: WebhookSubscribeResponse
Built with WebhookSubscribeParams.builder(). provider and calendarId are required. channelId is optional and generated by the server when omitted. expiration is an optional Unix timestamp in milliseconds.
Usage:
import com.mobiscroll.connect.Provider;
import com.mobiscroll.connect.models.WebhookSubscribeParams;
import com.mobiscroll.connect.models.WebhookSubscribeResponse;
WebhookSubscribeResponse subscription = client.webhooks().subscribeWebhook(
WebhookSubscribeParams.builder()
.provider(Provider.GOOGLE)
.calendarId("work@company.com")
.channelId("my-channel-123")
.build());
webhooks().unsubscribeWebhook
Removes an existing webhook subscription. See POST /unsubscribe-webhook.
Method: client.webhooks().unsubscribeWebhook(WebhookUnsubscribeParams params)
Returns: WebhookUnsubscribeResponse
Built with WebhookUnsubscribeParams.builder(). provider and channelId are required. resourceId is the provider resource ID returned by subscribeWebhook, where the provider uses one.
Usage:
import com.mobiscroll.connect.Provider;
import com.mobiscroll.connect.models.WebhookUnsubscribeParams;
client.webhooks().unsubscribeWebhook(
WebhookUnsubscribeParams.builder()
.provider(Provider.GOOGLE)
.channelId("my-channel-123")
.resourceId("resource-abc")
.build());