Organization connections
Authorize a Google Workspace or Microsoft 365 domain once, add its mailboxes as accounts, and open Microsoft 365 shared mailboxes.
An organization connection lets a Google Workspace or Microsoft 365 administrator authorize Workwae once. You then list the directory and add mailboxes by address. Each becomes an account that syncs, sends and announces account events like one a user connected, without any user signing in.
Choose an access method
| Google Workspace delegation | Microsoft 365 app-only | |
|---|---|---|
kind | google_delegation | microsoft_app |
| Credential | Your service account key | Your own app registration per cloud (client secret or certificate), or Workwae's app where Workwae runs one for that cloud |
| Administrator grants | Domain-wide delegation of the service account's client id, in the Admin console | Admin consent to the app |
| Required access | gmail.modify | Mail.ReadWrite, Mail.Send |
| Optional access | Calendar (on by default), contacts, admin.directory.user.readonly (with a directory admin) | Calendars.ReadWrite, MailboxSettings.Read, Contacts.ReadWrite, User.Read.All |
For a single Microsoft 365 shared mailbox, a user's own grant is enough. Gmail has no delegated shared mailboxes: use domain-wide delegation there.
A connection's status tracks whether it works:
status | Meaning |
|---|---|
pending_consent | No Microsoft directory has consented yet. |
unverified | Registered, or its credential or scopes changed, and not tested since. |
active | The last test or consent passed. |
failing | The last test failed; statusDetail names the failed check. |
A missing optional permission limits only calendar, contacts or the directory; mail keeps syncing. For Google, the administrator must authorize every scope the connection lists, or no token is issued.
Google Workspace delegation
Register the connection
Send the service account's JSON key file. Workwae checks and seals it, and returns the clientId and the google.scopes to authorize, in the Admin console's order.
jq -n --rawfile key service-account.json '{
kind: "google_delegation",
name: "Acme Workspace",
domain: "acme.com",
serviceAccountKey: $key,
calendarEnabled: true,
contactsEnabled: true,
directoryAdmin: "it-admin@acme.com"
}' | curl -X POST "https://api.workwae.com/v1/org-connections" \
-H "Authorization: Bearer $WORKWAE_API_KEY" \
-H "content-type: application/json" \
-d @-| Field | Meaning |
|---|---|
domain | The Workspace primary domain, whose directory is listed. |
calendarEnabled | Also authorize Google Calendar. Default true. |
contactsEnabled | Also authorize Google Contacts. Default false. |
directoryAdmin | A Workspace admin to impersonate for listing the directory; adds admin.directory.user.readonly. Omit it to add mailboxes by address only. |
Without calendar or contacts authorized, that sync fails for these accounts while mail keeps working. A key that is not a usable service account key answers 400 invalid_service_account_key.
Have the administrator authorize it
In the Google Admin console, under Security > API controls > Domain-wide delegation, the administrator adds connection.clientId with every scope in connection.google.scopes.
Test the connection
The test requests a token the way accounts will and reports each check. It impersonates user, or the directory admin by default; with neither, it answers 400 user_required.
curl -X POST "https://api.workwae.com/v1/org-connections/$CONNECTION_ID/test" \
-H "Authorization: Bearer $WORKWAE_API_KEY" \
-H "content-type: application/json" \
-d '{ "user": "ada@acme.com" }'A failed required check (isRequired) fails the test and leaves the connection failing.
Then add mailboxes.
Microsoft 365 app-only access
A Microsoft connection names its cloud: global (worldwide, including GCC; the default), gcc-high, dod or china (21Vianet). Sign-in and Graph calls stay in that cloud.
No app-only mail sync in 21Vianet
Graph in 21Vianet has no message delta yet, so app-only mail sync does not run there. Connect those mailboxes over IMAP and SMTP with each user's Microsoft sign-in (IMAP with OAuth).
Register the connection
credential names the app: { "type": "platform" } for Workwae's app, or your own registration with client_secret (clientId, clientSecret) or certificate (clientId, a PEM certificate and its unencrypted PKCS#8 privateKey).
curl -X POST "https://api.workwae.com/v1/org-connections" \
-H "Authorization: Bearer $WORKWAE_API_KEY" \
-H "content-type: application/json" \
-d '{
"kind": "microsoft_app",
"name": "Contoso (GCC High)",
"cloud": "gcc-high",
"credential": { "type": "client_secret", "clientId": "'"$CLIENT_ID"'", "clientSecret": "'"$CLIENT_SECRET"'" }
}'- The connection starts
pending_consent. With your own app already consented in the directory, pass itsdirectoryIdinstead; the connection startsunverifiedand you go straight to the test. connection.microsoft.permissionslists the Graph application permissions to grant, required ones first.- A platform connection refuses
directoryId(400directory_id_not_allowed), and a cloud Workwae runs no app in answers 400provider_not_configured. A bad certificate or key answers 400invalid_certificate.
Get admin consent
With your own app, start consent through the API and send a directory administrator to consentUrl. The browser returns to your redirectUri (on the allowlist) with status, connection_id, error and your state. The connection records the consenting directory and is tested.
curl -X POST "https://api.workwae.com/v1/org-connections/$CONNECTION_ID/consent" \
-H "Authorization: Bearer $WORKWAE_API_KEY" \
-H "content-type: application/json" \
-d '{ "redirectUri": "https://app.example.com/admin/consent-done", "state": "tenant-7" }'Consent to Workwae's own app is started from the console only (403 session_required through the API), so the directory is taken from the administrator who actually signed in.
Errors on return include access_denied, consent_failed, consent_unverified, directory_mismatch and directory_already_connected (another platform connection holds the directory).
Test the connection
Consent runs a test itself. Run POST /v1/org-connections/{connectionId}/test again after changing the credential; before any consent it answers 409 consent_required.
Add mailboxes
Both kinds list their directory and add mailboxes the same way.
GET /v1/org-connections/{connectionId}/directory-entriespages through the directory: up to 200 entries a page (default 100),querymatching the start of names and addresses. Each entry'saccountIdnames the account already added for it. AnextCursoris good for an hour, on this connection only.- Listing needs
User.Read.All(Microsoft) or a directory admin (Google). POST /v1/org-connections/{connectionId}/accountsadds up to 100 mailboxes per call, with optionalsettingsfor the new accounts.
curl -X POST "https://api.workwae.com/v1/org-connections/$CONNECTION_ID/accounts" \
-H "Authorization: Bearer $WORKWAE_API_KEY" \
-H "content-type: application/json" \
-d '{ "mailboxes": [{ "email": "support@acme.com" }, { "email": "sales@acme.com" }] }'Each mailbox is checked through the connection first (Google: a delegated token for it; Microsoft: its inbox). Every address gets its own result, in request order: created, exists (a live account was already there) or failed with the reason the API would answer:
| Reason | Meaning |
|---|---|
invalid_grant | No such user. |
consent_revoked | The scopes or permissions are not granted. |
provider_forbidden, provider_not_found | No mailbox access. |
plan_limit_accounts | The plan's account limit was reached part way through; the mailboxes before it are created. |
Shared mailboxes
Open a Microsoft 365 shared mailbox as an account of its own, through the grant of an account whose user can open it. Shared mailboxes need the shared mailboxes module in your plan (403 plan_module_unavailable).
Connect the delegating user with shared access
Connect the user's account with options.sharedMailboxesEnabled in a connect flow. It asks for Mail.ReadWrite.Shared and Mail.Send.Shared, plus the shared calendar and contacts permissions when those are on.
Open the shared mailbox
curl -X POST "https://api.workwae.com/v1/accounts/$USER_ACCOUNT_ID/shared-mailboxes" \
-H "Authorization: Bearer $WORKWAE_API_KEY" \
-H "content-type: application/json" \
-d '{ "email": "billing@contoso.com" }'The account reads the mailbox over Graph, or over Exchange IMAP when the delegating account uses IMAP.
| Error | Meaning |
|---|---|
400 scope_missing | The delegating account's grant lacks the shared scopes. Reconnect it with options.sharedMailboxesEnabled. |
400 shared_mailbox_unsupported | The delegating account is Gmail, password IMAP, itself shared or organization-authenticated, or the mailbox is its own. |
409 account_exists | The mailbox is already an account authenticated another way. |
422 shared_mailbox_inaccessible | The user cannot open the mailbox: no access, no such mailbox, or the login is refused. |
If the delegating account is deleted, the shared mailbox moves to reauth_required with delegate_missing.
When access changes
- When an administrator withdraws authorization, the affected accounts move to
reauth_requiredwithconsent_revoked, andaccount.reauth_requiredis sent. - Once access is restored, a passing test returns them to
activeand sendsaccount.connectedwithreauthorized: true. - Accounts added through a connection belong to it: a user sign-in cannot take them over. Fix the connection, then test it.
- Replacing a credential or the authorized scopes (
PATCH /v1/org-connections/{connectionId}) makes the connectionunverifieduntil the next test. - A connection cannot be deleted while live accounts use it (409
connection_in_use). Delete its accounts first.
Every route is in the Organization connections and Shared mailboxes references.