Skip to main content

OAuth API

Authorize​

Entry point for the OAuth2 authorization flow. Creates or retrieves the user in the database, loads any existing tokens, stores the OAuth request in a cookie, and redirects to the provider selection page where users can connect their calendar accounts.

info

This endpoint does not require authentication. It initiates the OAuth2 authorization flow.

Endpoint: GET /authorize

Request Parameters​

client_idstringRequired

Project/application identifier. This uniquely identifies your application in the Mobiscroll Connect system.

user_idstringRequired

Your own unique identifier for the user in your system who is being authorized.

scopestring

The scope of access requested from the user. Can be one of free-busy, read, or read-write. If not specified, defaults to read-write.

Default: read-write
redirect_uristring

Not used to choose the callback. Connect always redirects the user to the redirect URI registered for your application in the dashboard, with the authorization code; a redirect_uri sent in the query is replaced by the registered one. Send that same registered URI to POST /token.

Default: The registered redirect URI
statestring

Optional state parameter to maintain across the OAuth flow. This is passed back to your redirect_uri and can be used to prevent CSRF attacks and maintain application state.

Default: undefined
lngstring

Language for the Connect pages (provider selection, consent, login, and error pages). See Supported languages for the values Connect accepts. When omitted, the UI falls back to the browser's Accept-Language header, then English. Arabic (ar), Hebrew (he) and Persian (fa) render right-to-left. Example: ?lng=es.

Default: Accept-Language, then en
providersstring

Comma-separated list of the providers to offer on the provider selection page, for example providers=google,microsoft. Accepted values: google, microsoft, apple, caldav; unknown values are ignored, and when none is valid all providers are shown. display_providers is accepted as an alias.

Default: All providers
themestring

Color scheme of the Connect pages: light, dark or auto (follows the device setting). It takes precedence over your application's default theme — see Theme precedence.

Default: auto
response_typestring

OAuth2 response type. Typically set to "code" for the authorization code flow.

Default: undefined

Response​

Redirect302 - Redirect

Redirects to the provider selection page (/authorize) with all query parameters preserved, and redirect_uri set to your application's registered redirect URI. The provider selection page allows the user to:

  1. Choose which calendar provider(s) to connect (Google Calendar, Microsoft Outlook, Apple Calendar, CalDAV)
  2. Authenticate with each selected provider
  3. Review and approve the OAuth consent screen
  4. Complete the OAuth authorization flow

After the user successfully authenticates and approves the authorization, they will be redirected back to your application's redirect_uri with:

  • code: The authorization code that can be exchanged for an access token (this is the user_id value)
  • state: The state parameter you provided (if any), for CSRF protection

Example redirect to your application:

https://app.example.com/callback?code=user-456&state=xyz789

Error Responses​

  • 400 - Bad Request. Error creating or retrieving user
  • 401 - Unauthorized. Invalid or unrecognized client_id
  • 500 - Internal Server Error. Database or unexpected error

Examples​

Initiate OAuth authorization
GET /authorize?client_id=proj-123&user_id=user-456&redirect_uri=https://app.example.com/callback&response_type=code&state=xyz789&scope=read-write

# Optional: append &lng=es to localize the Connect pages,
# &theme=dark to set their color scheme, or &providers=google,microsoft to limit the providers offered
Redirects to authorization page
302 Redirect
Location: /authorize?client_id=proj-123&user_id=user-456&redirect_uri=https://app.example.com/callback&response_type=code&state=xyz789&scope=read-write

Set-Cookie: oauth_req={"client_id":"proj-123","user_id":"user-456",...}; HttpOnly; Path=/; Max-Age=7200; SameSite=Lax
info
  • The client_id is validated against the database before proceeding
  • If the client_id is invalid, the user is redirected to an error page
  • The user is always redirected to the redirect URI registered for the client_id; a redirect_uri in the query is not used
  • The OAuth request is stored in a cookie to maintain state across the authorization flow
  • If the user already exists in the database, their existing information is retrieved
  • Any existing provider tokens are loaded and initialized in the token store
  • The cookie expires after 2 hours if the flow is not completed
  • All query parameters are passed to the provider selection page, with redirect_uri replaced by the registered one
Localizing the Connect pages

Pass lng on the authorize URL to render the Connect pages (provider selection, consent, login, and error pages) in a specific language. See Supported languages for the current list.

  • If lng is omitted, the UI falls back to the browser's Accept-Language header, then English.
  • Arabic (ar), Hebrew (he) and Persian (fa) are right-to-left; the UI direction switches automatically.
  • Example: /authorize?client_id=...&user_id=...&lng=es. In the SDKs, pass lng to the auth-URL builder (see the examples above).
Personalizing the Connect pages

Set a Display Name for your application in the dashboard. When set, the authorization and connection-management screens are personalized with it (e.g. "<Display Name> is using Mobiscroll Connect ..." and a "Back to <Display Name>" button) so users always know which app they are connecting to. When no display name is set, a generic wording is shown. No request parameter is required - the display name is resolved from the client_id.

Re-authorizing an existing user

Calling /authorize again for a user_id that already has tokens does not revoke any existing access or refresh tokens. The existing tokens are loaded and remain valid. The authorization flow simply allows the user to connect additional calendar provider accounts on top of the ones already connected.

To fully invalidate a user's tokens before re-authorization, call POST /revoke first.


Token management​

Token Lifetimes​

The following table summarizes the lifetime of every credential issued by Mobiscroll Connect.

Token / CredentialLifetimeNotes
Authorization code10 minutesSingle-use — deleted on first exchange
Access token (JWT)1 hour (3600 s)exp claim embedded in the JWT; rejected immediately after expiry even if the signature is valid
Refresh tokenNo embedded expiryThe refresh token does not expire after a given time. Validity enforced server-side via JTI rotation (rotation happens on refresh call, not by time). Remains valid until rotated (plus a 30-second grace period) or explicitly revoked. If refresh returns invalid_grant or the newly issued refresh token is lost before persistence, the user must re-authorize.
OAuth session cookie2 hoursoauth_req cookie that carries the in-progress authorization state
Refresh Token Rotation

Every POST /token call with grant_type=refresh_token issues a new refresh token and invalidates the previous one after a 30-second grace period. Presenting a replaced refresh token after that returns invalid_grant, and presenting one from two or more rotations back revokes the user's authorization — see Refreshing from several instances. Calling /revoke invalidates the entire token family (all access tokens and refresh tokens for that user/client pair) at once.

Refreshing from several instances​

When your application runs as more than one process — several containers or pods, a process manager in cluster mode, serverless functions, background workers — two of them can refresh the same user's token at nearly the same moment. Connect accepts this within limits:

The refresh token you presentResult
The current one200 — a new access token and a new refresh token
The one it replaced, within 30 seconds of the replacement200 — a new access token and a refresh token interchangeable with the one the first request received. Every instance keeps working, and it does not matter which of the returned refresh tokens you store
The one it replaced, more than 30 seconds later403 invalid_grant. Nothing is revoked — load the current token from your storage and retry
One from two or more rotations back403 invalid_grant, and Connect treats it as a copied token being reused: it revokes the authorization (every refresh token and access token for that user). The user must re-authorize

The last row is why every instance must work from the current token rather than its own copy:

  1. Store tokens where all your instances read them — a database or a shared cache, not only each process's memory.
  2. Give each client the current tokens from that storage when it starts a job or handles a request, rather than one copy loaded at startup.
  3. Write the new tokens back as soon as a refresh returns.

You do not need a lock around refreshes. The official SDKs also share one refresh between concurrent calls on the same client instance, but they do not coordinate across processes — see each SDK's integration guide for persisting refreshed tokens and loading them back.

Handling token expiry on API calls​

All protected API endpoints (/events, /calendars, /subscribe-webhook, etc.) validate the Authorization: Bearer <access_token> header. When the access token is missing, expired, or revoked the server returns 401 Unauthorized.

Recommended handling flow:

API call
└─► 401 Unauthorized
└─► POST /token (grant_type=refresh_token)
├─► 200 OK → persist new tokens, retry original request
└─► 403 invalid_grant → user must re-authorize (redirect to GET /authorize)
tip

Implement a single request-retry wrapper (or an HTTP interceptor) that catches 401 responses, attempts one token refresh, and only falls back to re-authorization if the refresh itself fails.


Get Access Token​

Exchanges an authorization code for an access token. This is the token endpoint of the OAuth2 authorization code flow.

info

This endpoint requires client authentication using either HTTP Basic authentication or client credentials in the request body.

Endpoint: POST /token

Authentication​

This endpoint supports two authentication methods:

HTTP Basic Authentication:

Authorization: Basic base64(client_id:client_secret)

Request Body Authentication:

  • Include client_id and client_secret in the request body

Request Parameters​

grant_typestringRequired

The OAuth2 grant type. Must be set to "authorization_code" for the authorization code flow.

codestringRequired

The authorization code received from the authorization endpoint. This code is the user_id and is returned to your redirect_uri after the user completes the authorization flow.

redirect_uristringRequired

The redirect URI used in the authorization request. This must match exactly with the redirect URI used when obtaining the authorization code.

client_idstring

Your application's client identifier. Required if not using HTTP Basic authentication.

Default: undefined
client_secretstring

Your application's client secret. Required if not using HTTP Basic authentication.

Default: undefined

Response​

access_tokenstring

The access token (JWT) that can be used to authenticate API requests. This token contains the user ID, client ID, and project ID in the payload.

What is an Access Token?

An access token is a credential (usually a string) that your application receives after a successful authentication. It is used to authorize API requests on behalf of a user. In Mobiscroll Connect, the access token is a JWT (JSON Web Token).

What is a JWT (JSON Web Token)?

A JWT is a secure, compact, URL-safe token format that encodes claims about the user and the application. Mobiscroll Connect uses JWTs as access tokens, which include information such as the user ID, client ID, and expiration time. The server verifies the JWT to ensure requests are authentic and authorized.

token_typestring

The type of token returned. Always "Bearer".

refresh_tokenstring

A refresh token that can be used to obtain a new access token when the current one expires. Refresh tokens do not expire on their own — they remain valid until used or explicitly revoked. Each use of a refresh token issues a new access token and a new refresh token (token rotation), and the previous refresh token is invalidated after a 30-second grace period. Store this token securely on the server side.

What is a Refresh Token?

An access token is short-lived (1 hour). Rather than forcing the user to log in again every hour, the server issues a refresh token alongside the access token. Your application stores the refresh token securely (server-side, never in the browser) and uses it — silently, without user interaction — to obtain a fresh access token whenever the current one expires.

If you lose the refresh token (e.g. it was never persisted, or was overwritten before the new one was saved), the user must complete the full OAuth authorization flow again to restore access.

Refresh Token Rotation

Mobiscroll Connect implements refresh token rotation: every time you exchange a refresh token for a new access token, the server issues a brand-new refresh token and invalidates the old one after 30 seconds. Presenting the old one after that is rejected with invalid_grant, and presenting one from two or more rotations back also revokes the user's authorization — see Refreshing from several instances.

The response does not include expires_in. Access tokens are valid for 1 hour; to schedule a refresh, read the exp claim of the access token (see JWT Token Payload), or refresh when an API call returns 401.

Error Responses​

Error bodies carry error (one of the codes below) and a human-readable error_description.

  • 400 - Bad Request
    • invalid_request - Missing required parameters, for example code
  • 401 - Unauthorized
    • invalid_client - Client authentication failed
  • 403 - Forbidden
    • invalid_grant - Invalid authorization code, code expired, client mismatch, or redirect URI mismatch
  • 500 - Internal Server Error
    • server_error - The request could not be completed; retry it
  • 501 - Not Implemented
    • unsupported_grant_type - grant_type is not authorization_code or refresh_token

Token Validation​

The endpoint performs the following validations:

  1. Authorization code exists - The code must be valid and found in the database
  2. Client match - The client_id must match the one used to obtain the code
  3. Redirect URI match - The redirect_uri must exactly match the original request
  4. Code expiration - The authorization code expires after 10 minutes
  5. Single use - The authorization code is deleted after successful exchange

JWT Token Payload​

The access token is a JWT containing:

{
"sub": "user_id", // User identifier
"aud": "client_id", // Client/Project identifier
"projectId": "client_id", // Project identifier
"scope": "read-write", // Granted scope
"jti": "uuid", // Unique token ID
"exp": 1234567890 // Expiration timestamp (1 hour from issuance)
}

The refresh token is a JWT containing:

{
"sub": "user_id", // User identifier
"aud": "client_id", // Client/Project identifier
"projectId": "client_id", // Project identifier
"type": "refresh", // Token type discriminator
"jti": "uuid", // Unique token ID (used for rotation tracking)
// No "exp" — validity is managed server-side via the database
}

Examples​

Exchange authorization code for access token (HTTP Basic Auth)
POST /token
Authorization: Basic YmFzZTY0X2VuY29kZWRfY3JlZGVudGlhbHM=
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code&code=user-456&redirect_uri=https://app.example.com/callback
Exchange authorization code for access token (Body Auth)
POST /token
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code&code=user-456&redirect_uri=https://app.example.com/callback&client_id=proj-123&client_secret=secret-xyz
Success Response
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "Bearer",
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}
Error Response - Invalid Code
{
"error": "invalid_grant",
"error_description": "Invalid authorization code"
}
Error Response - Code Expired
{
"error": "invalid_grant",
"error_description": "Authorization code expired"
}
Error Response - Client Mismatch
{
"error": "invalid_grant",
"error_description": "Client mismatch"
}
info
  • Authorization codes expire after 10 minutes
  • Authorization codes are single-use and deleted after successful exchange
  • Access tokens expire after 1 hour
  • A refresh token is always returned alongside the access token; store it securely server-side
  • The access token is a JWT that must be included as a Bearer token in all subsequent API requests
  • Client credentials can be provided via HTTP Basic authentication or in the request body
  • The redirect_uri must exactly match the one used in the authorization request
  • Revoked access tokens are rejected immediately even if the JWT signature is still valid

Refresh Access Token​

Obtains a new access token using a refresh token. Each call also issues a new refresh token and invalidates the previously used one after a 30-second grace period (refresh token rotation).

info

This endpoint requires client authentication using either HTTP Basic authentication or client credentials in the request body.

Endpoint: POST /token

Authentication​

Same as Get Access Token — HTTP Basic or request body credentials.

Request Parameters​

grant_typestringRequired

Must be "refresh_token".

refresh_tokenstringRequired

The refresh token obtained from a previous token exchange.

client_idstring

Your application's client identifier. Required if not using HTTP Basic authentication.

Default: undefined
client_secretstring

Your application's client secret. Required if not using HTTP Basic authentication.

Default: undefined

Response​

access_tokenstring

A new access token (JWT) valid for 1 hour.

token_typestring

Always "Bearer".

refresh_tokenstring

A new refresh token. The previously used refresh token stops working 30 seconds later. Store this new token in place of the old one.

Scope inheritance

The new access token carries the same scope as the original authorization. Scope cannot be upgraded through token refresh — the user must re-authorize (via GET /authorize) to request a different scope.

Persist the new refresh token before using it

Once this endpoint responds successfully, the old refresh token keeps working for only 30 seconds. You must durably persist the new refresh token before discarding the old one. If your application crashes, loses the response, or fails to save the token to your database, and does not retry within those 30 seconds:

  • The old token is invalidated — it cannot be reused.
  • The new token is lost — it was only ever returned in this response.
  • The user must re-authorize (complete the full OAuth flow again) to restore access.

To mitigate this, persist the new token atomically (e.g. in a database transaction) before acknowledging success. If saving fails, repeat the refresh with the old token straight away — within the 30 seconds it returns a working refresh token again.

Concurrent refreshes with the same token are safe; refreshing with an outdated copy is not. See Refreshing from several instances.

The response does not include expires_in; the new access token is valid for 1 hour (its exp claim).

Error Responses​

  • 400 - Bad Request
    • invalid_request - refresh_token is missing
  • 401 - Unauthorized
    • invalid_client - Client authentication failed
  • 403 - Forbidden
    • invalid_grant - Refresh token not found, revoked, replaced more than 30 seconds ago, client mismatch, or wrong token type. A refresh token from two or more rotations back also revokes the user's authorization — see Refreshing from several instances
  • 500 - Internal Server Error
    • server_error - The request could not be completed; retry it with the same refresh token
  • 501 - Not Implemented
    • unsupported_grant_type - grant_type is not authorization_code or refresh_token

Examples​

Refresh access token (HTTP Basic Auth)
POST /token
Authorization: Basic YmFzZTY0X2VuY29kZWRfY3JlZGVudGlhbHM=
Content-Type: application/x-www-form-urlencoded

grant_type=refresh_token&refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Success Response
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "Bearer",
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}
Error Response - Revoked or Invalid Token
{
"error": "invalid_grant",
"error_description": "Invalid refresh token"
}
info
  • Always replace the stored refresh token with the new one returned in the response
  • Concurrent refresh attempts with the same token all succeed when they arrive within 30 seconds of the first one
  • Presenting a replaced refresh token more than 30 seconds later is rejected with invalid_grant; presenting one from two or more rotations back also revokes the user's authorization
  • Refresh tokens are managed server-side; they have no embedded expiry date

Get Connection Status​

Returns the calendar accounts the user has connected, grouped by provider, with the consent and health state of each account. Use it to show the user which accounts are connected, to find the id to pass to POST /disconnect, and to find accounts that need to be reconnected.

info

This endpoint requires a valid access token in the Authorization: Bearer <access_token> header. Refresh tokens are not accepted.

Endpoint: GET /connection-status

Response​

connectionsobject

Connected accounts keyed by provider. The object always contains the google, microsoft, apple and caldav keys; a provider with no connected account has an empty array. Each account object contains:

idstring

Account identifier, usually the account's email address. Pass it as account to POST /disconnect to disconnect only this account.

displaystring

Label to show for the account. Currently the same value as id.

grantedScopesstring[]

The scopes the provider actually granted for this account, which are not necessarily the scopes Connect asked for. Empty for Apple and CalDAV, which authenticate with a username and app password. See Partial consent.

calendarPermissionGrantedboolean | null

Whether the granted scopes cover calendar access at the scope of the access token used for this request. false means the account connected but withheld calendar access, so it can list no calendars until the user reconnects. null for Apple and CalDAV, and for accounts with no recorded scopes. See Partial consent.

syncState'active' | 'reauth_required'

Whether the stored provider credentials still work. reauth_required means the provider has rejected them and the account's calendars are not syncing; the user has to connect the account again. See Connection health.

syncStateUpdatedAtstring | null

ISO 8601 timestamp of the last change to syncState, or null if it has never changed.

limitReachedboolean

true when your application has reached the connected-account limit of the free Develop tier. Accounts are counted across all users of your application. On other plans this is always false.

limitnumber

The connected-account limit of the free Develop tier. On other plans this is always 0.

Error Responses​

  • 401 - Unauthorized. The access token is missing, invalid, expired or revoked, or a refresh token was sent. The error field describes the reason.

Examples​

Get connection status
GET /connection-status
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Success Response
{
"connections": {
"google": [
{
"id": "user@gmail.com",
"display": "user@gmail.com",
"grantedScopes": ["openid", "https://www.googleapis.com/auth/userinfo.email", "https://www.googleapis.com/auth/calendar"],
"calendarPermissionGranted": true,
"syncState": "active",
"syncStateUpdatedAt": null
}
],
"microsoft": [
{
"id": "user@outlook.com",
"display": "user@outlook.com",
"grantedScopes": ["openid", "profile", "https://graph.microsoft.com/Calendars.ReadWrite"],
"calendarPermissionGranted": true,
"syncState": "reauth_required",
"syncStateUpdatedAt": "2026-09-01T10:00:00.000Z"
}
],
"apple": [],
"caldav": []
},
"limitReached": false,
"limit": 0
}
info
  • Accounts in the reauth_required state stay listed, so you can show them and prompt the user to reconnect
  • Disconnected accounts are not listed
  • The SDKs return limitReached but not limit

Disconnect Account​

Disconnects one calendar account of the user, or every account of one provider. Connect stops the webhook subscriptions of the account's calendars, deletes the stored provider credentials, and marks the account as disconnected: it no longer appears in the connection status and no longer counts toward your plan's connected-account limit.

Disconnect does not revoke the user's Connect access and refresh tokens. To invalidate those, use POST /revoke — see Disconnect vs Revoke.

info

This endpoint requires a valid access token in the Authorization: Bearer <access_token> header. Expired and revoked access tokens and refresh tokens are not accepted. The parameters are sent in the query string; the request body is empty.

Endpoint: POST /disconnect

Query Parameters​

providerstringRequired

The provider of the account to disconnect: google, microsoft, apple or caldav.

accountstring

The id of the account to disconnect, as returned by GET /connection-status (connections[provider][].id). Omit it to disconnect every account of the provider.

Default: Every account of the provider

Response​

Returns 200 OK when the account is disconnected. The request is idempotent: disconnecting an account that is not connected also returns 200.

Success Response
{
"success": true
}

Error Responses​

  • 400 - Bad Request
    • Missing or invalid provider - provider is missing or is not one of google, microsoft, apple, caldav
    • User not found - this user has never authorized your application
  • 401 - Unauthorized. The access token is missing, invalid, expired or revoked, or a refresh token was sent. The error field describes the reason.

Examples​

Disconnect one Google account
POST /disconnect?provider=google&account=user@gmail.com
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Disconnect every Microsoft account of the user
POST /disconnect?provider=microsoft
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Success Response
{
"success": true
}
Error Response - Invalid Provider
{
"error": "Missing or invalid provider"
}
info
  • A calendar that another still-connected account of the same provider can also see (for example, a shared calendar) keeps its webhook subscription
  • Deliveries already waiting for a retry can still arrive for up to about 27.6 hours
  • The user's Connect access and refresh tokens stay valid. Use POST /revoke to invalidate them
  • The grant at the provider is not revoked: your application stays listed in the user's Google or Microsoft account permissions until the user removes it there
  • To connect the account again, send the user through GET /authorize

Revoke Token​

Revokes every access token and refresh token issued to the user for your application. Use it when the user signs out of your application, or to invalidate tokens that may have been exposed.

Revoke does not disconnect calendar accounts: they stay connected, and webhook deliveries for their calendars continue. To remove a calendar account, use POST /disconnect — see Disconnect vs Revoke.

info

This endpoint does not require authentication. The token itself is used to identify the user and client.

Endpoint: POST /revoke

Request Body​

tokenstringRequired

Either an access token or a refresh token. An expired access token is accepted too. The server decodes it to extract the user and client identifiers, then revokes all active tokens for that user/client combination.

Response​

Returns 200 OK even if the token was already invalid or revoked — this prevents leaking information about token state.

Success Response
{
"success": true
}

Error Responses​

  • 400 - Bad Request. token field is missing or not a string.
Error Response - Missing Token
{
"error": "invalid_request",
"error_description": "token is required"
}

Examples​

Revoke tokens
POST /revoke
Content-Type: application/json

{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}
Success Response
{
"success": true
}
info
  • Both access tokens and refresh tokens are accepted, including an expired access token — the endpoint revokes the entire token family regardless of which token type is provided
  • The response is always 200 with { "success": true } to avoid leaking information about token validity
  • Called without a token, the SDKs revoke their stored refresh token (or, if there is none, the stored access token) and clear their stored credentials. When you pass a token, the stored credentials are kept. With no token passed and none stored, the SDKs raise a validation error without sending a request
  • After revocation, the user must complete the OAuth authorization flow again to obtain new tokens. Their calendar accounts are still connected, so re-authorizing the same user_id makes them available again

Disconnect vs Revoke​

Disconnect and revoke remove different things, and neither does the other's job.

Disconnect — POST /disconnectRevoke — POST /revoke
RemovesOne calendar account, or every account of one provider: its stored provider credentials and the webhook subscriptions of its calendarsEvery Connect access token and refresh token of the user
Keeps workingThe user's Connect tokens and their other connected accounts. The grant at Google or Microsoft stays in place until the user removes it thereAll connected calendar accounts and their webhook deliveries. The accounts still count toward your plan's connected-account limit
AuthenticationA valid access tokenThe token to revoke; an expired access token or a refresh token is accepted
Use it whenThe user removes a calendar account from your applicationThe user signs out of your application, or tokens may have been exposed

To remove a user from your application completely, first call disconnect for each provider that has connected accounts in the connection status, then call revoke. Disconnect needs a valid access token, so after revoke it is rejected with 401.