OAuth lets you connect a provider by signing in to it, instead of pasting an API token. Tried runs the authorization code flow, stores the tokens encrypted with the connection, and refreshes them before they expire.
Providers#
| Provider | Used for | PKCE | Scopes requested |
|---|---|---|---|
hubspot |
CRM sync, apps | No | crm.objects.contacts.read crm.objects.contacts.write crm.schemas.contacts.read |
salesforce |
CRM sync, apps | Yes | api refresh_token |
dynamics365 |
CRM sync, apps | Yes | <instance_url>/user_impersonation offline_access |
zoho |
CRM sync, apps | No | ZohoCRM.modules.ALL,ZohoCRM.settings.fields.READ,ZohoCRM.users.READ |
gohighlevel |
CRM sync, apps | No | contacts.readonly contacts.write locations.readonly locations/customFields.readonly |
cal_com |
Apps | Yes | READ_BOOKING WRITE_BOOKING READ_PROFILE |
google_drive |
Synced sources | Yes | https://www.googleapis.com/auth/drive.readonly |
onedrive |
Synced sources | Yes | Files.Read.All offline_access |
notion |
Synced sources | No | none (Notion asks the user which pages to share) |
Calendly and Zendesk connect with pasted credentials only.
Deployment setup Needs keys#
Each provider needs an OAuth client registered with the vendor, and its id and secret in the API's environment:
| Provider | Environment variables |
|---|---|
| HubSpot | HUBSPOT_OAUTH_CLIENT_ID, HUBSPOT_OAUTH_CLIENT_SECRET |
| Salesforce | SALESFORCE_OAUTH_CLIENT_ID, SALESFORCE_OAUTH_CLIENT_SECRET |
| Dynamics 365 | DYNAMICS365_OAUTH_CLIENT_ID, DYNAMICS365_OAUTH_CLIENT_SECRET |
| Zoho | ZOHO_OAUTH_CLIENT_ID, ZOHO_OAUTH_CLIENT_SECRET |
| GoHighLevel | GOHIGHLEVEL_OAUTH_CLIENT_ID, GOHIGHLEVEL_OAUTH_CLIENT_SECRET |
| Cal.com | CALCOM_OAUTH_CLIENT_ID, CALCOM_OAUTH_CLIENT_SECRET |
| Google Drive | GOOGLE_DRIVE_OAUTH_CLIENT_ID, GOOGLE_DRIVE_OAUTH_CLIENT_SECRET |
| OneDrive | ONEDRIVE_OAUTH_CLIENT_ID, ONEDRIVE_OAUTH_CLIENT_SECRET |
| Notion | NOTION_OAUTH_CLIENT_ID, NOTION_OAUTH_CLIENT_SECRET |
Optional per provider: <PREFIX>_OAUTH_AUTHORIZE_URL and <PREFIX>_OAUTH_TOKEN_URL override the vendor endpoints.
Register this redirect URI with every vendor app:
https://api.tried.com.au/oauth/callbackGET /oauth/providers returns the exact redirect URI for your deployment.
Without a client id, a provider uses the built-in mock OAuth provider. Its consent page says "Mock OAuth provider" and "No real account is used". Connections created through it stay in mock mode.
Connect from the dashboard#
CRM: Settings, Integrations, CRM sync, then Connect on the provider's card. After a successful connection the card shows Reconnect.
Apps: Connect app, choose the App, then Connect with HubSpot account (the label names the chosen app). For an existing app, open it and press Connect with OAuth (or Reconnect with OAuth).
Synced sources: in a knowledge base, Connect a folder, pick the provider, then Connect Google Drive (or OneDrive, Notion).
For Dynamics 365 the dashboard first asks for your organisation URL, for example https://contoso.crm.dynamics.com.
After consent, you return to the page you started from with a toast: "HubSpot connected" or "Couldn't connect HubSpot" (named after the provider), with the vendor's error.
Connect with the API#
POST /oauth/start returns the URL to send the user's browser to. Admin or developer role.
| Field | Type | Required | Description |
|---|---|---|---|
target |
string | Yes | crm, app or kb_sync |
provider |
string | Yes | One of the providers above. Must be valid for the target |
app_id |
string | No | app only: reconnect this existing app. Without it a new app is created |
name |
string | No | app only: name for the new app |
knowledge_base_id |
string | kb_sync |
The knowledge base to sync into |
sync_config |
object | kb_sync |
{"folder_id"} for Google Drive (required), {"drive_id", "folder_id"} for OneDrive (optional), {"page_id"} for Notion (required) |
sync_id |
string | No | kb_sync only: reconnect an existing sync instead of creating one |
options.instance_url |
string | Dynamics 365 | Organisation URL |
options.tenant |
string | No | Microsoft tenant id or domain for Dynamics 365 and OneDrive |
options.login_url |
string | No | Salesforce login host, for example https://test.salesforce.com for sandboxes |
options.accounts_url |
string | No | Zoho accounts host for your data centre, for example https://accounts.zoho.com.au |
return_to |
string | No | Dashboard path to return to. Default /settings/integrations (or /knowledge-base for kb_sync) |
curl -X POST https://api.tried.com.au/oauth/start \
-H "Authorization: Bearer $TRIED_API_KEY" \
-H "Content-Type: application/json" \
-d '{"target": "crm", "provider": "salesforce", "options": {"login_url": "https://test.salesforce.com"}}'{
"authorize_url": "https://test.salesforce.com/services/oauth2/authorize?response_type=code&client_id=3MVG9...&redirect_uri=https%3A%2F%2Fapi.tried.com.au%2Foauth%2Fcallback&state=...&scope=api+refresh_token&code_challenge=...&code_challenge_method=S256",
"mock": false,
"provider": "salesforce",
"pkce": true
}After consent the vendor redirects to /oauth/callback, which stores the tokens and redirects the browser to the dashboard with query parameters:
| Parameter | When |
|---|---|
oauth=success or oauth=error |
Always |
provider |
Always |
message |
On error |
app_id |
App connections |
sync_id, knowledge_base_id |
Synced source connections |
What a successful connection creates#
| Target | Result |
|---|---|
crm |
The provider's CRM connection is created or updated, enabled, with sync_calls and sync_contacts on |
app |
A new integration app (or the app_id you passed) with status connected |
kb_sync |
A new synced source (or the sync_id you passed). The first sync starts right away |
Values the vendor returns with the token are kept with it: Salesforce instance_url, Zoho api_domain, GoHighLevel location_id, Notion workspace id.
Security#
- State: the
stateparameter is encrypted, expires after 10 minutes and can be used once. - PKCE: S256 for the providers marked above (and always for the mock provider).
- Storage: tokens are encrypted inside the connection's credentials and never returned by the API.
Token refresh#
- Before each use, a token with less than 5 minutes left is refreshed first. Only one refresh runs per connection at a time, across API instances.
- Every minute, connections expiring within 10 minutes are refreshed in the background.
- Notion tokens do not expire and have no refresh token.
Force a refresh with POST /oauth/refresh:
curl -X POST https://api.tried.com.au/oauth/refresh \
-H "Authorization: Bearer $TRIED_API_KEY" \
-H "Content-Type: application/json" \
-d '{"target": "crm", "provider": "hubspot"}'{ "refreshed": true, "expires_at": 1760690400000 }Pass {"target": "app", "app_id": "app_..."} for an app. A connection that was set up with a pasted token returns 400 "This connection does not use OAuth".