How to Set Up an OAuth 2.0 Client for Your App
Set up an OAuth 2.0 client in BreezeDoc so your app can act for other BreezeDoc users: create the client in API Settings, then use the authorization code flow.
Prerequisites
- Plan: Individual Plan or Agency Plan (both one-time purchases), including the Agency Plan free trial. The Free Plan does not include API access.
- Account: An active BreezeDoc account, logged in.
- Application: A web application with a server-side component and a redirect (callback) URL.
- Technical knowledge: The OAuth 2.0 authorization code flow.
What is OAuth 2.0?
OAuth 2.0 is the industry-standard protocol for authorization. With BreezeDoc's OAuth support:
- User authorization - users grant your app access to their BreezeDoc account
- No password sharing - your app never sees the user's password
- Full account access - BreezeDoc has no fine-grained scopes; an access token has the same access as the user who authorized it
- Token refresh - a refresh token gets new access tokens without asking the user again
- Multi-user support - one application serves many BreezeDoc users
- Revocable access - users can revoke your app at any time
OAuth 2.0 vs Personal Access Tokens
Use OAuth 2.0 when:
- Building applications for multiple BreezeDoc users
- Distributing your application to others
- Creating public integrations
- Building SaaS products that integrate with BreezeDoc
Use Personal Access Tokens when:
- Building personal scripts or internal tools
- Only accessing your own BreezeDoc account
- Prototyping and testing
- Running server-to-server integrations you control
Creating an OAuth Client
Step-by-Step Instructions
- Log in to your BreezeDoc account.
- Open Settings and click Integrations in the side menu.
- In the API section, click Manage API Keys. The API Settings page opens.
- In the OAuth Clients section, click Create New Client.

- In the Create Client window, enter:
- Name: your application's name ("Something your users will recognize and trust.")
- Redirect URL: your application's authorization callback URL, for example
https://yourapp.com/auth/breezedoc/callback
- Click Create.
- The client appears in the OAuth Clients table with its Client ID, Name, and Secret. Both the ID and the secret stay visible there, so you can copy them at any time - keep the secret on your server only.
Understanding the Redirect URL
The redirect URL is where BreezeDoc sends users after they authorize your application.
- Exact match: the
redirect_uriin your authorization request must match a registered redirect URL exactly. - Several URLs: you can register more than one redirect URL for a client by separating them with commas. Using a separate client per environment (development, staging, production) keeps their credentials apart.
- Use HTTPS in production; plain HTTP is only sensible for local development.
- Examples:
- Production:
https://yourapp.com/auth/breezedoc/callback - Development:
http://localhost:3000/auth/breezedoc/callback - Staging:
https://staging.yourapp.com/auth/breezedoc/callback
- Production:
Implementing the OAuth Flow
Authorization Code Flow Overview
- User initiates: the user clicks "Connect BreezeDoc" in your app.
- Authorization request: your app redirects the user to the BreezeDoc authorization URL.
- User approves: the user grants your app access to their BreezeDoc account.
- Authorization code: BreezeDoc redirects back to your app with a code.
- Exchange code for tokens: your server exchanges the code for an access token and a refresh token.
- Access the API: use the access token to make API requests for that user.
- Refresh: use the refresh token to get a new access token before it expires.
Step 1: Redirect the User to the Authorization URL
GET https://breezedoc.com/oauth/authorize? client_id=YOUR_CLIENT_ID& redirect_uri=YOUR_REDIRECT_URL& response_type=code& scope=*& state=RANDOM_STRING
Parameters:
- client_id: your OAuth Client ID from BreezeDoc
- redirect_uri: a registered redirect URL (must match exactly)
- response_type: always
code - scope: use
*; BreezeDoc has no individual scopes, and every token gets the authorizing user's access - state (recommended): a random string that protects against CSRF attacks
Step 2: Receive the Authorization Code
After the user approves, BreezeDoc redirects to your callback URL:
https://yourapp.com/auth/breezedoc/callback? code=AUTHORIZATION_CODE& state=YOUR_STATE_VALUE
Check that state matches the value you sent, then read the code parameter.
Step 3: Exchange the Code for an Access Token
From your server, send a POST request:
POST https://breezedoc.com/oauth/token
Content-Type: application/json
{
"grant_type": "authorization_code",
"client_id": "YOUR_CLIENT_ID",
"client_secret": "YOUR_CLIENT_SECRET",
"redirect_uri": "YOUR_REDIRECT_URL",
"code": "AUTHORIZATION_CODE"
}
Response:
{
"token_type": "Bearer",
"expires_in": 31536000,
"access_token": "eyJ0eXAiOiJKV1...",
"refresh_token": "def50200a8f7..."
}
Important:
- Store the
access_tokenandrefresh_tokensecurely (encrypted in your database), linked to the user. - Never expose tokens to client-side code.
Step 4: Make API Requests
curl -X GET https://breezedoc.com/api/me \ -H "Authorization: Bearer ACCESS_TOKEN" \ -H "Content-Type: application/json"
All endpoints are listed in BreezeDoc API: Getting Started, Endpoints and Limits; rate limits are in API Authentication and Rate Limits: Tokens, OAuth and Errors.
Step 5: Refresh the Access Token
Access tokens and refresh tokens are each valid for one year. Before the access token expires, request a new one:
POST https://breezedoc.com/oauth/token
Content-Type: application/json
{
"grant_type": "refresh_token",
"refresh_token": "YOUR_REFRESH_TOKEN",
"client_id": "YOUR_CLIENT_ID",
"client_secret": "YOUR_CLIENT_SECRET",
"scope": "*"
}
Response: a new access_token and refresh_token - replace the old values.
Managing OAuth Clients
Viewing Your OAuth Clients
- Go to Settings → Integrations → Manage API Keys.
- The OAuth Clients table shows each client's Client ID, Name, and Secret.
Editing an OAuth Client
- Find the client in the OAuth Clients table and click Edit.
- Change the Name or Redirect URL.
- Click Save Changes.
Note: The Client ID and secret cannot be changed. For new credentials, create a new client and delete the old one.
Deleting an OAuth Client
- Find the client in the OAuth Clients table.
- Click Delete. The client is deleted immediately - there is no confirmation step.
Warning: Deleting a client revokes every access token issued to it. All users who authorized your application lose the connection and must authorize a new client.
Managing Authorized Applications
Revoking an Application's Access
Users can see and revoke the applications connected to their account:
- Go to Settings → Integrations → Manage API Keys.
- Find the Authorized Applications section.
- Click Revoke next to an application. Its access token and refresh token stop working immediately.
Security Best Practices
Client Secret Security
- Never commit it to version control: use environment variables or a secrets manager
- Server-side only: never put the client secret in client-side code
- Rotate: replace old clients with new ones periodically
- Separate environments: use a different client for development, staging, and production
Token Security
- Encrypt tokens at rest in your database
- HTTPS only for all API requests
- Keep tokens out of the browser: do not store them in localStorage or sessionStorage
- Handle expiry: refresh tokens before they expire
- Log API calls in your application for security auditing
State Parameter (CSRF Protection)
- Generate a random string before redirecting to the authorization URL.
- Store it in the session or an encrypted cookie.
- Include it as
statein the authorization URL. - When the user returns, check that the returned
statematches, and reject the request if it does not.
Code Examples
Example: Node.js with Express
const express = require('express');
const axios = require('axios');
const app = express();
const CLIENT_ID = process.env.BREEZEDOC_CLIENT_ID;
const CLIENT_SECRET = process.env.BREEZEDOC_CLIENT_SECRET;
const REDIRECT_URI = 'https://yourapp.com/auth/breezedoc/callback';
// Step 1: Redirect to BreezeDoc
app.get('/auth/breezedoc', (req, res) => {
const state = generateRandomString(); // Implement this
req.session.oauth_state = state;
const authUrl = `https://breezedoc.com/oauth/authorize?client_id=${CLIENT_ID}&redirect_uri=${REDIRECT_URI}&response_type=code&scope=*&state=${state}`;
res.redirect(authUrl);
});
// Steps 2 and 3: Handle the callback and exchange the code
app.get('/auth/breezedoc/callback', async (req, res) => {
const { code, state } = req.query;
// Verify state
if (state !== req.session.oauth_state) {
return res.status(403).send('Invalid state parameter');
}
try {
const response = await axios.post('https://breezedoc.com/oauth/token', {
grant_type: 'authorization_code',
client_id: CLIENT_ID,
client_secret: CLIENT_SECRET,
redirect_uri: REDIRECT_URI,
code: code
});
const { access_token, refresh_token } = response.data;
// Store tokens securely (encrypt in database)
await saveUserTokens(req.user.id, access_token, refresh_token);
res.redirect('/dashboard');
} catch (error) {
res.status(500).send('Authorization failed');
}
});
Example: Python with Flask
from flask import Flask, redirect, request, session
import requests
import os
app = Flask(__name__)
app.secret_key = os.environ['SECRET_KEY']
CLIENT_ID = os.environ['BREEZEDOC_CLIENT_ID']
CLIENT_SECRET = os.environ['BREEZEDOC_CLIENT_SECRET']
REDIRECT_URI = 'https://yourapp.com/auth/breezedoc/callback'
@app.route('/auth/breezedoc')
def authorize():
state = generate_random_string() # Implement this
session['oauth_state'] = state
auth_url = f"https://breezedoc.com/oauth/authorize?client_id={CLIENT_ID}&redirect_uri={REDIRECT_URI}&response_type=code&scope=*&state={state}"
return redirect(auth_url)
@app.route('/auth/breezedoc/callback')
def callback():
code = request.args.get('code')
state = request.args.get('state')
# Verify state
if state != session.get('oauth_state'):
return 'Invalid state parameter', 403
response = requests.post('https://breezedoc.com/oauth/token', json={
'grant_type': 'authorization_code',
'client_id': CLIENT_ID,
'client_secret': CLIENT_SECRET,
'redirect_uri': REDIRECT_URI,
'code': code
})
data = response.json()
# Store tokens securely
save_user_tokens(session['user_id'], data['access_token'], data['refresh_token'])
return redirect('/dashboard')
Troubleshooting
I get "invalid_client" ("Client authentication failed")
- Check the Client ID and client secret; the secret goes in
client_secret, not the ID. - The same error appears when the
redirect_uridoes not exactly match a registered redirect URL - check trailing slashes, HTTP vs HTTPS, and the path. Fix the URL with Edit in the OAuth Clients table if needed. - Check that the client has not been deleted.
Every API request returns 401 even though I have a token
- BreezeDoc API endpoints act for a user, so the token must belong to one. A token from the
client_credentialsgrant has no user and is rejected with 401 Unauthorized on every endpoint. - Use the authorization code flow above, or a personal access token for your own account.
My access token stopped working (401 Unauthorized)
- Check whether the token has expired, and refresh it with the refresh token.
- Use the exact header format:
Authorization: Bearer TOKEN. - Check whether the user revoked your application under Authorized Applications, or whether the client was deleted - the user then has to authorize your app again.
I cannot find the OAuth Clients section
- API access, including OAuth, requires the Individual Plan or the Agency Plan. See which plan you are on under Settings → Plan; see How to Upgrade Your BreezeDoc Plan.
- If you just upgraded, reload the page.
Still stuck? Email support@breezedoc.com.
Frequently Asked Questions
How many OAuth clients can I create?
There is no limit. Create separate clients for different environments or different applications.
Can I change my Client ID or client secret?
No. Both are permanent for each client. For new credentials, create a new client and delete the old one - deleting it revokes the tokens it issued.
How long do access tokens last?
Access tokens expire after one year (31,536,000 seconds), and refresh tokens are also valid for one year. Refresh the access token before it expires.
What scopes are available?
BreezeDoc does not define individual scopes. Request scope=*; every token has the same access as the user who authorized it.
Can I use OAuth in a single-page application?
Not directly: the flow needs your client secret, which cannot be kept safe in browser code. Run the OAuth flow on your backend and keep the tokens there.
What happens if a user revokes my application?
The revoked access token and its refresh token stop working immediately, and your API requests for that user return 401 Unauthorized. Ask the user to authorize your application again.
Need more help? Contact our support team at support@breezedoc.com - we are here to help!