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 delegationMicrosoft 365 app-only
kindgoogle_delegationmicrosoft_app
CredentialYour service account keyYour own app registration per cloud (client secret or certificate), or Workwae's app where Workwae runs one for that cloud
Administrator grantsDomain-wide delegation of the service account's client id, in the Admin consoleAdmin consent to the app
Required accessgmail.modifyMail.ReadWrite, Mail.Send
Optional accessCalendar (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:

statusMeaning
pending_consentNo Microsoft directory has consented yet.
unverifiedRegistered, or its credential or scopes changed, and not tested since.
activeThe last test or consent passed.
failingThe 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 @-
FieldMeaning
domainThe Workspace primary domain, whose directory is listed.
calendarEnabledAlso authorize Google Calendar. Default true.
contactsEnabledAlso authorize Google Contacts. Default false.
directoryAdminA 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 its directoryId instead; the connection starts unverified and you go straight to the test.
  • connection.microsoft.permissions lists the Graph application permissions to grant, required ones first.
  • A platform connection refuses directoryId (400 directory_id_not_allowed), and a cloud Workwae runs no app in answers 400 provider_not_configured. A bad certificate or key answers 400 invalid_certificate.

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-entries pages through the directory: up to 200 entries a page (default 100), query matching the start of names and addresses. Each entry's accountId names the account already added for it. A nextCursor is good for an hour, on this connection only.
  • Listing needs User.Read.All (Microsoft) or a directory admin (Google).
  • POST /v1/org-connections/{connectionId}/accounts adds up to 100 mailboxes per call, with optional settings for 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:

ReasonMeaning
invalid_grantNo such user.
consent_revokedThe scopes or permissions are not granted.
provider_forbidden, provider_not_foundNo mailbox access.
plan_limit_accountsThe 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.

ErrorMeaning
400 scope_missingThe delegating account's grant lacks the shared scopes. Reconnect it with options.sharedMailboxesEnabled.
400 shared_mailbox_unsupportedThe delegating account is Gmail, password IMAP, itself shared or organization-authenticated, or the mailbox is its own.
409 account_existsThe mailbox is already an account authenticated another way.
422 shared_mailbox_inaccessibleThe 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_required with consent_revoked, and account.reauth_required is sent.
  • Once access is restored, a passing test returns them to active and sends account.connected with reauthorized: 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 connection unverified until 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.

On this page