Bridge
Bridge runs Gmail, Google Drive, Slack, and Granola connections for your users under your own OAuth apps. Your backend calls the API with one organization API key; users consent on Google or Slack and return to your platform.
OAuth setup
Register your apps once per organization under Bridge → Apps in the dashboard. Each card's dialog shows the redirect URI, the scopes, and the Slack manifest to copy.
| App | Secrets you enter | Where they come from |
|---|---|---|
| Gmail | Client ID, Client secret, Return URL | A Google Cloud OAuth client ID (Web application) with the Gmail API enabled and the dialog's redirect URI authorized. Scope gmail.readonly. |
| Google Drive | Client ID, Client secret, Return URL | Same, with the Google Drive API enabled and the Drive redirect URI authorized. Scope drive.readonly. |
| Slack | Client ID, Client secret, Signing secret, Return URL | A Slack app created From a manifest with the manifest in the dialog. Credentials are under Basic Information → App Credentials. |
| Granola | None | Each user pastes a personal Granola API key into your platform. |
Gmail and Google Drive are separate apps: enter one Google client on both cards or a different client on each. A shared client needs both redirect URIs authorized. Publish the Google consent screen; an app left in Testing gets refresh tokens that expire after 7 days.
Return URL
The page on your platform where users land after consent, for example
https://platform.example.com/connected (http://localhost is allowed for development). The
callback appends these query parameters:
| Parameter | Value |
|---|---|
provider | gmail, google_drive, or slack |
status | connected or error |
installation_id | The new installation, when status=connected. Store it against your user. |
error | Why consent failed, when status=error |
error | Meaning |
|---|---|
access_denied | The user declined consent |
missing_code | The provider returned no authorization code |
account_already_connected | Gmail and Drive: the Google account is connected in another Mixedbread organization. Disconnect it there first. |
workspace_already_connected | Slack: the workspace is connected in another Mixedbread organization |
reconnect_account_mismatch | Gmail: a reconnect used a different Google account |
invalid_state | The consent link expired or was reused. Start again. |
token_exchange_failed | Google rejected the code. Check the client secret and the redirect URI. |
callback_failed | Anything else |
API reference
OAuth clients
| Endpoint | Method | Path |
|---|---|---|
| List OAuth Clients | GET | /v1/integrations/oauth-clients |
| Register OAuth Client | PUT | /v1/integrations/oauth-clients/{provider} |
| Delete OAuth Client | DELETE | /v1/integrations/oauth-clients/{provider} |
| List OAuth Client Connections | GET | /v1/integrations/oauth-clients/{provider}/connections |
Connect
| Endpoint | Method | Path |
|---|---|---|
| Authorize Gmail | GET | /v1/integrations/gmail/oauth/authorize |
| Authorize Google Drive | GET | /v1/integrations/google_drive/oauth/authorize |
| Authorize Slack | GET | /v1/integrations/slack/oauth/authorize |
| Connect Granola | POST | /v1/integrations/granola/connect |
Installations
| Endpoint | Method | Path |
|---|---|---|
| List Installations | GET | /v1/integrations/installations |
| Get Installation | GET | /v1/integrations/installations/{installation_id} |
| Disconnect Installation | DELETE | /v1/integrations/installations/{installation_id} |
| Sync Installation | POST | /v1/integrations/installations/{installation_id}/sync |
| Update Granola Schedule | PUT | /v1/integrations/granola/schedule |
| Replace Resources | PUT | /v1/integrations/installations/{installation_id}/resources |
| List Failed Resources | GET | /v1/integrations/installations/{installation_id}/resources/failed |
| Refresh Resources | POST | /v1/integrations/installations/{installation_id}/resources/refresh |
Stores
| Endpoint | Method | Path |
|---|---|---|
| Get Store | GET | /v1/stores/{store_identifier} |
| List Store Files | POST | /v1/stores/{store_identifier}/files/list |
| Search Chunks | POST | /v1/stores/search |
Gmail
curl -s -o /dev/null -w '%{redirect_url}' \
'https://api.mixedbread.com/v1/integrations/gmail/oauth/authorize?store_name=mail-42' \
-H 'Authorization: Bearer YOUR_API_KEY'Redirect the browser to the printed URL. store_name names the user's store; omit it for
gmail-<email>.
The browser arrives at
https://platform.example.com/connected?provider=gmail&status=connected&installation_id=….
Store installation_id against your user, then read the store id:
curl https://api.mixedbread.com/v1/integrations/installations/INSTALLATION_ID \
-H 'Authorization: Bearer YOUR_API_KEY'{
"data": {
"installation_id": "2f1c9a8e-6b4d-4e3a-9c1f-7d8e5b6a4c21",
"provider": "gmail",
"status": "active",
"account": { "id": "olivia@example.com", "name": "olivia@example.com", "email": "olivia@example.com", "workspace": null },
"store_id": "5d3e7f9a-1b2c-4d6e-8f0a-9c8b7a6d5e4f",
"store_name": "mail-42",
"sync": { "failed_resource_count": 0, "last_error": null, "last_error_operation": null, "last_error_at": null }
}
}Keep installation_id and store_id per user. Mixedbread keeps no link between your users
and their installations.
curl -X POST https://api.mixedbread.com/v1/integrations/installations/INSTALLATION_ID/sync \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer YOUR_API_KEY' \
-d '{"sync_all": true}'Every message outside spam, trash, and drafts is imported, and new mail is picked up every ten minutes. To sync selected labels instead, list them and replace the selection:
curl https://api.mixedbread.com/v1/integrations/installations/INSTALLATION_ID/resources \
-H 'Authorization: Bearer YOUR_API_KEY'
curl -X PUT https://api.mixedbread.com/v1/integrations/installations/INSTALLATION_ID/resources \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer YOUR_API_KEY' \
-d '{"resource_ids": ["INBOX", "Label_12"]}'# installation status and account-level errors
curl https://api.mixedbread.com/v1/integrations/installations/INSTALLATION_ID \
-H 'Authorization: Bearer YOUR_API_KEY'
# per-resource outcome in resources[].sync.last_run
curl https://api.mixedbread.com/v1/integrations/installations/INSTALLATION_ID/resources \
-H 'Authorization: Bearer YOUR_API_KEY'
# resources whose last job failed
curl https://api.mixedbread.com/v1/integrations/installations/INSTALLATION_ID/resources/failed \
-H 'Authorization: Bearer YOUR_API_KEY'
# files ingested so far, in file_counts
curl https://api.mixedbread.com/v1/stores/STORE_ID \
-H 'Authorization: Bearer YOUR_API_KEY'status: "needs_reauth" on the installation means the user must consent again: repeat step 1
with installation_id=INSTALLATION_ID so the same installation is reused. last_run.status is
in_progress, completed, failed, or stale; a large mailbox stays in_progress across
slices until the label is done. To list individual files, use
List Store Files.
curl -X POST https://api.mixedbread.com/v1/stores/search \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer YOUR_API_KEY' \
-d '{"query": "renewal terms", "store_identifiers": ["STORE_ID"], "top_k": 10}'Pass only the store ids that belong to the signed-in user. The organization key can search every store.
Google Drive
Same flow as Gmail with /v1/integrations/google_drive/oauth/authorize. Sync the whole Drive:
curl -X POST https://api.mixedbread.com/v1/integrations/installations/INSTALLATION_ID/sync \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer YOUR_API_KEY' \
-d '{"sync_all": true}'Differences:
sync_allruns one job over the whole Drive, Shared with me included, and registers push notifications for changes.queued_resource_idsis empty and the root folders keeplast_run: null; watchfile_countson the store instead.- Resources are hierarchical: list a folder's children with
?parent_id=FOLDER_ID. Selecting a folder syncs its subtree. - Files Drive cannot export (unsupported types, empty files) are skipped, not failed.
- Reconnect after
needs_reauthby repeating step 1 with the same Google account.
Slack
Same flow with /v1/integrations/slack/oauth/authorize. Sync every conversation the user is in:
curl -X POST https://api.mixedbread.com/v1/integrations/installations/INSTALLATION_ID/sync \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer YOUR_API_KEY' \
-d '{"sync_all": true}'Differences:
- Users connect as themselves (
access_mode=user, the default), each with their own installation and store.account.workspaceholds the workspace name. sync_allcovers every conversation the user is a member of: channels, private channels, group messages, and direct messages. Public channels they have not joined are skipped withnot_a_member.- Right after connecting, the conversation catalog is still being built: the listing reports
listing_status: "loading"andsync_allis applied when the walk completes. Refresh Resources rebuilds the catalog later, for example after the user joins channels. - New messages arrive through Slack events verified with the signing secret you registered.
Granola
No OAuth. Connect with the user's API key and state who it belongs to:
curl -X POST https://api.mixedbread.com/v1/integrations/granola/connect \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer YOUR_API_KEY' \
-d '{
"api_key": "GRANOLA_API_KEY",
"account_name": "Olivia Chen",
"account_email": "olivia@example.com",
"store_name": "notes-42"
}'data.id is the installation id and data.metadata.granola_ingestion_store_id the store id; an
invalid key returns 422. Sync every note:
curl -X POST https://api.mixedbread.com/v1/integrations/installations/INSTALLATION_ID/sync \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer YOUR_API_KEY' \
-d '{"sync_all": true}'Then monitor and search as in steps 4 and 5 of the Gmail flow. Differences:
sync_allimports every note the key can see and polls every ten minutes. Change the cadence with Update Granola Schedule:off,ten_minutes,hourly,daily, orweekly.- Resources are folders, listed hierarchically like Drive.
- Personal keys expire on Granola's side. The installation then records
sync.last_errorand the user connects again with a new key.
Disconnect
curl -X DELETE https://api.mixedbread.com/v1/integrations/installations/INSTALLATION_ID \
-H 'Authorization: Bearer YOUR_API_KEY'Stops the sync and revokes the grant at the provider. Google revokes the whole authorization for a client, so a Google grant is kept while another installation of the same account uses the same OAuth client, for example Gmail and Drive on one client. The store and its files remain; delete the store separately if the data should go too.
Legacy Embedding and Reranking Models
Find the maintained resources for earlier Mixedbread embedding and reranking models and migrate to current search workflows.
Connectors
Connect Slack, Google Drive, Gmail, Granola, Linear, Notion, and Salesforce to keep Mixedbread Stores synchronized with your source data.