OAuth Apps
Build third-party integrations with OAuth 2.0
Build third-party applications that integrate with Coherence using OAuth 2.0. OAuth apps allow your users to securely authorize access to their Coherence data without sharing credentials.
Overview
OAuth 2.0 is the industry-standard protocol for authorization. When you build an OAuth app, users can grant your application access to their Coherence workspace data. Your app receives tokens to make API requests on their behalf.
Use Cases
- SaaS integrations - Connect Coherence to your product
- Marketplace apps - Build apps for all Coherence users
- Automation tools - Create workflows that act on user data
- Analytics platforms - Access data for reporting and insights
- Mobile apps - Build native experiences with Coherence data
Registering an App
Create Your App
- Go to Settings > Developers > OAuth Apps
- Click Create App
- Fill in your app details:
| Field | Description |
|---|---|
| App Name | Display name shown to users during authorization |
| Description | Brief description of what your app does |
| Website URL | Your app's homepage |
| Privacy Policy URL | Link to your privacy policy (required for public apps) |
| Terms of Service URL | Link to your terms (required for public apps) |
Configure Redirect URIs
Add one or more redirect URIs where users will be sent after authorization:
https://yourapp.com/oauth/callback
https://localhost:3000/oauth/callback (for development)
Redirect URIs must use HTTPS in production. HTTP is only allowed for localhost during development.
Select Scopes
Choose the permissions your app needs. Request only the scopes necessary for your app's functionality.
Get Your Credentials
After creating your app, you'll receive:
- Client ID - Public identifier for your app (e.g.,
cid_abc123xyz) - Client Secret - Private key for token exchange (e.g.,
cs_secret_xyz789)
Your client secret is shown only once. Store it securely - you cannot retrieve it later. If compromised, rotate it immediately in your app settings.
OAuth 2.0 Flow
Step 1: Authorization Request
Redirect users to the authorization URL:
https://app.getcoherence.io/oauth/authorize
?client_id=YOUR_CLIENT_ID
&redirect_uri=https://yourapp.com/oauth/callback
&response_type=code
&scope=read:records write:records
&state=random_state_string
| Parameter | Required | Description |
|---|---|---|
client_id | Yes | Your app's client ID |
redirect_uri | Yes | Must match a registered redirect URI |
response_type | Yes | Always code for authorization code flow |
scope | Yes | Space-separated list of requested scopes |
state | Recommended | Random string to prevent CSRF attacks |
Step 2: User Consent
Users see a consent screen displaying:
- Your app name and logo
- Requested permissions
- Approve/Deny buttons
If the user approves, they're redirected to your callback URL with an authorization code:
https://yourapp.com/oauth/callback?code=auth_code_abc123&state=random_state_string
If denied:
https://yourapp.com/oauth/callback?error=access_denied&error_description=User+denied+access
Step 3: Exchange Code for Tokens
Exchange the authorization code for access and refresh tokens:
curl -X POST "https://api.getcoherence.io/oauth/token" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=authorization_code" \
-d "client_id=YOUR_CLIENT_ID" \
-d "client_secret=YOUR_CLIENT_SECRET" \
-d "code=auth_code_abc123" \
-d "redirect_uri=https://yourapp.com/oauth/callback"Response:
{
"access_token": "at_eyJhbGciOiJIUzI1NiIs...",
"refresh_token": "rt_dGhpcyBpcyBhIHJlZnJl...",
"token_type": "Bearer",
"expires_in": 3600,
"scope": "read:records write:records",
"workspace_id": "ws_abc123"
}Authorization codes expire after 10 minutes and can only be used once.
Available Scopes
Request only the scopes your app needs. Users can see what permissions you're requesting.
Record Scopes
| Scope | Description |
|---|---|
read:records | Read records from all modules |
write:records | Create and update records |
delete:records | Delete records |
Module Scopes
| Scope | Description |
|---|---|
read:modules | View module schemas and fields |
write:modules | Create and modify modules (admin only) |
User Scopes
| Scope | Description |
|---|---|
read:users | View workspace users and teams |
read:profile | Access the authorizing user's profile |
Activity Scopes
| Scope | Description |
|---|---|
read:activity | View activity feed and audit logs |
Communication Scopes
| Scope | Description |
|---|---|
read:email | Read email conversations |
send:email | Send emails on behalf of the user |
Webhook Scopes
| Scope | Description |
|---|---|
read:webhooks | View webhook configurations |
write:webhooks | Create and manage webhooks |
Token Management
Token Expiration
| Token Type | Expiration |
|---|---|
| Access Token | 1 hour (3600 seconds) |
| Refresh Token | 30 days |
Refreshing Access Tokens
When an access token expires, use the refresh token to get a new one:
curl -X POST "https://api.getcoherence.io/oauth/token" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=refresh_token" \
-d "client_id=YOUR_CLIENT_ID" \
-d "client_secret=YOUR_CLIENT_SECRET" \
-d "refresh_token=rt_dGhpcyBpcyBhIHJlZnJl..."Response:
{
"access_token": "at_bmV3IGFjY2VzcyB0b2tl...",
"refresh_token": "rt_bmV3IHJlZnJlc2ggdG9r...",
"token_type": "Bearer",
"expires_in": 3600,
"scope": "read:records write:records"
}Store the new refresh token - it replaces the previous one. Refresh tokens are rotated for security.
Revoking Tokens
Users can revoke access from their Coherence settings. You can also programmatically revoke tokens:
curl -X POST "https://api.getcoherence.io/oauth/revoke" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "client_id=YOUR_CLIENT_ID" \
-d "client_secret=YOUR_CLIENT_SECRET" \
-d "token=at_eyJhbGciOiJIUzI1NiIs..."This revokes both the access token and its associated refresh token.
Making Authenticated Requests
Include the access token in the Authorization header:
curl -X GET "https://api.getcoherence.io/v1/modules/contacts/records" \
-H "Authorization: Bearer at_eyJhbGciOiJIUzI1NiIs..." \
-H "Content-Type: application/json"Example: Create a Record
curl -X POST "https://api.getcoherence.io/v1/modules/contacts/records" \
-H "Authorization: Bearer at_eyJhbGciOiJIUzI1NiIs..." \
-H "Content-Type: application/json" \
-d '{
"name": "Jane Smith",
"email": "[email protected]",
"company": "Acme Inc"
}'Example: Get Current User
curl -X GET "https://api.getcoherence.io/v1/users/me" \
-H "Authorization: Bearer at_eyJhbGciOiJIUzI1NiIs..."Response:
{
"data": {
"id": "usr_abc123",
"email": "[email protected]",
"name": "Jane Smith",
"workspace_id": "ws_xyz789"
}
}Handling Token Refresh in Your App
Implement automatic token refresh to ensure uninterrupted access:
interface TokenData {
accessToken: string;
refreshToken: string;
expiresAt: number;
}
class CoherenceClient {
private tokenData: TokenData;
private clientId: string;
private clientSecret: string;
async request(endpoint: string, options: RequestInit = {}) {
// Check if token is expired or will expire soon (5 min buffer)
if (Date.now() >= this.tokenData.expiresAt - 300000) {
await this.refreshAccessToken();
}
const response = await fetch(`https://api.getcoherence.io/v1${endpoint}`, {
...options,
headers: {
...options.headers,
'Authorization': `Bearer ${this.tokenData.accessToken}`,
'Content-Type': 'application/json',
},
});
// Handle token expiration during request
if (response.status === 401) {
await this.refreshAccessToken();
return this.request(endpoint, options);
}
return response.json();
}
private async refreshAccessToken() {
const response = await fetch('https://api.getcoherence.io/oauth/token', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({
grant_type: 'refresh_token',
client_id: this.clientId,
client_secret: this.clientSecret,
refresh_token: this.tokenData.refreshToken,
}),
});
const data = await response.json();
this.tokenData = {
accessToken: data.access_token,
refreshToken: data.refresh_token,
expiresAt: Date.now() + (data.expires_in * 1000),
};
// Persist updated tokens to your storage
await this.saveTokens(this.tokenData);
}
}App Review Process
Development Mode
New apps start in development mode:
- Only workspace admins can authorize
- Limited to 10 authorized users
- Not listed in the app marketplace
Submitting for Review
To make your app public:
- Go to Settings > Developers > OAuth Apps
- Select your app
- Click Submit for Review
- Provide:
- App description and screenshots
- Video demo (optional but recommended)
- Support contact information
- Data handling practices
Review Criteria
Apps are reviewed for:
- Security - Proper handling of tokens and user data
- Functionality - App works as described
- User Experience - Clear authorization flow
- Compliance - Privacy policy and terms are complete
Review typically takes 3-5 business days.
After Approval
Approved apps can:
- Be installed by any Coherence user
- Be listed in the app marketplace (optional)
- Have unlimited authorized users
Testing in Sandbox
Sandbox Environment
Use our sandbox for development and testing:
Authorization: https://sandbox.app.getcoherence.io/oauth/authorize
Token endpoint: https://sandbox.api.getcoherence.io/oauth/token
API base URL: https://sandbox.api.getcoherence.io/v1
Creating Sandbox Apps
- Create a separate OAuth app for sandbox testing
- Use sandbox redirect URIs (can include localhost)
- Test the full authorization flow
Sandbox data is reset weekly. Do not use sandbox for production workloads.
Test Users
Create test users in your sandbox workspace to simulate different permission scenarios.
Security Best Practices
Store Secrets Securely
Do:
- Store client secrets in environment variables or secret managers
- Use encrypted storage for access and refresh tokens
- Implement proper access controls for token storage
Don't:
- Commit secrets to version control
- Log tokens or secrets
- Store secrets in client-side code
Use PKCE for Mobile and SPA
For public clients (mobile apps, single-page apps), use PKCE (Proof Key for Code Exchange):
import crypto from 'crypto';
// Generate code verifier (43-128 characters)
const codeVerifier = crypto.randomBytes(32).toString('base64url');
// Generate code challenge
const codeChallenge = crypto
.createHash('sha256')
.update(codeVerifier)
.digest('base64url');Include in authorization request:
https://app.getcoherence.io/oauth/authorize
?client_id=YOUR_CLIENT_ID
&redirect_uri=https://yourapp.com/callback
&response_type=code
&scope=read:records
&code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM
&code_challenge_method=S256
Include verifier in token exchange:
curl -X POST "https://api.getcoherence.io/oauth/token" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=authorization_code" \
-d "client_id=YOUR_CLIENT_ID" \
-d "code=auth_code_abc123" \
-d "redirect_uri=https://yourapp.com/callback" \
-d "code_verifier=dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk"Validate Redirect URIs
- Register all valid redirect URIs in your app settings
- Use exact matching - avoid wildcards
- Validate the
stateparameter on callback
Handle Errors Gracefully
async function handleCallback(code: string, state: string) {
// Verify state matches what you sent
if (state !== storedState) {
throw new Error('Invalid state parameter - possible CSRF attack');
}
try {
const tokens = await exchangeCodeForTokens(code);
return tokens;
} catch (error) {
if (error.code === 'invalid_grant') {
// Code expired or already used
return redirectToAuthorization();
}
throw error;
}
}Token Security Checklist
- Store tokens encrypted at rest
- Use secure, httpOnly cookies for web apps
- Implement token rotation on refresh
- Set appropriate token expiration
- Revoke tokens when users disconnect
- Monitor for suspicious token usage
Related: Authentication | API Overview