Connect flows
Hosted connect pages, headless OAuth, refresh-token import, your own OAuth apps and reauthorization.
Three ways to connect
| Flow | When to use it |
|---|---|
| Hosted connect session | workwae hosts the provider choice, the OAuth hand-off and the IMAP form. You create a session and send the user to its URL. |
| Headless OAuth | Your own UI picks the provider; workwae returns the provider's authorization URL and handles the callback. |
| Refresh-token import | You already hold a refresh token (from your own OAuth app) and hand it over without any user interaction. |
The two browser flows end the same way: the user returns to your redirectUri, which must be on the tenant allowlist. A hosted session may leave it out; the user then ends on a workwae confirmation page and you read the outcome from the session.
Redirect URI allowlist
settings.redirectUrislists up to 50 addresses. Edit it on the board's Settings page or withPATCH /v1/settings(admin scope).- Entries must be absolute https URLs; plain http is accepted only for localhost and 127.0.0.1.
- A redirect URI is allowed when its origin and path exactly match an entry. The query string and fragment are yours to vary.
- Anything else answers 400 with reason
redirect_uri_not_allowed. A session whose address was removed from the list afterwards stops working.
Hosted connect sessions
| Field | Meaning |
|---|---|
providers | What the page offers: google, microsoft and imap (the IMAP/SMTP form for any other mailbox). Defaults to Google and Microsoft. |
microsoftCloud | The Microsoft cloud for sign-in with the platform app (see below). |
oauthAppIds | Your own OAuth apps to use instead of the platform apps, per provider. |
redirectUri | Where the user returns with the outcome. Optional: without it they see a workwae confirmation page, which needs no allowlist entry. |
loginHint | The address the user should connect. It prefills the provider sign-in and the IMAP form. |
externalRef | Your identifier, stored on the account. |
state | Echoed back to the redirect URI, unchanged. |
accountId | Re-authorize this existing account instead of creating a new one. |
options | Settings for the new account: calendarEnabled, bodyStorage, and mailAccess (imap reads the mailbox over IMAP with the OAuth grant, for Google only through your own Google OAuth app; see the IMAP guide). |
ttlSeconds | How long the link works: 60 seconds to 24 hours, one hour by default. A session can be completed once. |
The return trip
The browser comes back to your redirect URI with the outcome in the query string:
?status=success&account_id=<id>&state=<your state>
?status=error&error=<code>&state=<your state>Without a redirectUri, follow the session instead: GET /v1/connect-sessions/{sessionId} answers its status (pending, completed or failed), the error code of a failure, and the accountId and email it connected.
| error | Meaning |
|---|---|
access_denied | The user declined the consent screen. |
scope_denied | The user unticked a permission the account needs. |
session_used | The session was already completed. |
session_expired | The link was opened after ttlSeconds. |
provider_not_allowed | The session does not offer the provider that was picked. |
plan_limit_accounts | Your plan's account limit is reached. Delete an account or raise the plan, then send a new link. |
account_exists | The mailbox is already connected. Create a reconnect session (accountId) to replace its sign-in. |
account_mismatch | A reconnect signed in with a different mailbox than the account being reconnected. |
account_not_found | The account being reconnected was deleted meanwhile. |
missing_refresh_token | The provider issued no refresh token, so the account could not be kept signed in. Try again; the user may need to remove the app's earlier grant first. |
token_exchange_failed | The provider refused to exchange the sign-in for tokens. |
identity_failed | The provider did not tell us which mailbox signed in. |
unknown_oauth_app | Your own OAuth app for this provider was removed before the sign-in finished. |
google_imap_requires_own_app | Google over IMAP was asked for through the workwae platform app, which connects Gmail through the Gmail API only. Use your own Google OAuth app. |
billing_past_due | Your payment is past due beyond the grace period, so no new account is connected. |
provider_error | The provider reported an error of its own. |
internal_error | Something failed on our side; the attempt can be repeated with a new link. |
Treat a code not listed here as a failed attempt: new codes may be added. If the return address is no longer in your allowed redirect URIs, the user sees an error page instead, since there is nowhere safe to send them.
Check state against what you sent, then use the account id. The account.connected event announces the same account to your webhooks.
Headless OAuth
With your own provider picker, ask for the authorization URL directly and redirect the user to it. The callback, the account creation and the return to your redirect URI work exactly as for hosted sessions.
The URL is a page on the API origin that names your organization and the provider; the user continues to the provider with its button. Continuing works once and binds the sign-in to that browser with an HttpOnly cookie. Redirect the user's own browser to it; a callback arriving in any other browser gets an error page and connects nothing.
curl -X POST "https://api.workwae.com/v1/oauth-authorizations" \
-H "Authorization: Bearer $WORKWAE_API_KEY" \
-H "content-type: application/json" \
-d '{
"provider": "microsoft",
"redirectUri": "https://app.example.com/mail/connected",
"state": "csrf-token"
}'
# => { "authorizeUrl": "https://api.workwae.com/connect/authorize?key=..." }Import a refresh token
When your own OAuth app already obtained a refresh token with the mail (and calendar) scopes, connect the account without a browser. Name the app with oauthAppId (registered below); omit it for tokens issued to the platform app.
curl -X POST "https://api.workwae.com/v1/accounts" \
-H "Authorization: Bearer $WORKWAE_API_KEY" \
-H "content-type: application/json" \
-d '{
"provider": "google",
"refreshToken": "1//0g...",
"oauthAppId": "6f1c2a4e-8b3d-4e5f-9a7b-1c2d3e4f5a6b",
"externalRef": "user_42"
}'Your own OAuth apps
By default users sign in through the platform's Google and Microsoft apps. Register your own to show your name on the consent screen and own the grant. Register these redirect URIs with the provider:
https://api.workwae.com/connect/callback/google
https://api.workwae.com/connect/callback/microsoftcurl -X POST "https://api.workwae.com/v1/oauth-apps" \
-H "Authorization: Bearer $WORKWAE_API_KEY" \
-H "content-type: application/json" \
-d '{
"provider": "microsoft",
"cloud": "global",
"name": "Example Mail",
"clientId": "00000000-0000-0000-0000-000000000000",
"clientSecret": "...",
"authority": "common"
}'- Microsoft apps need a
cloud: each cloud needs its own app registration.authorityis the directory (tenant) id, orcommon. - Use an app by passing its id as
oauthAppIds(hosted sessions) oroauthAppId(headless and import). - Google apps can push Gmail changes instead of polling: create a topic and an authenticated push subscription in the app's Cloud project and set
gmailPush(topic,serviceAccount) withPATCH /v1/oauth-apps/{appId}. The subscription delivers to the app'sgmailPushEndpoint, which is also the token audience. Settings > OAuth apps shows a ready gcloud script. - An app cannot be deleted while accounts still use it.
Microsoft clouds
| microsoftCloud | Cloud |
|---|---|
global | Microsoft 365, including GCC (moderate). The default. |
gcc-high | US Government GCC High. |
dod | US Government DoD. |
china | Microsoft 365 operated by 21Vianet. |
microsoftCloud applies to sign-ins with the platform app; a tenant app carries its own cloud. The account reports it as cloud.
Reauthorization
When a provider revokes a grant (a password change, an admin removing consent), the account moves to reauth_required and an account.reauth_required event names the reason. Send the user through a connect flow again with the account's id:
curl -X POST "https://api.workwae.com/v1/connect-sessions" \
-H "Authorization: Bearer $WORKWAE_API_KEY" \
-H "content-type: application/json" \
-d '{
"redirectUri": "https://app.example.com/mail/connected",
"accountId": "6f1c2a4e-8b3d-4e5f-9a7b-1c2d3e4f5a6b"
}'- The session signs in to the account's own provider, cloud, OAuth app and mail access;
providersmust include that provider. A password IMAP account gets the IMAP form. - The headless flow takes
accountIdtoo, with the account's address as the sign-in hint. - On success the same account becomes active again and
account.connectedis sent withreauthorized: true.