Overview

This guide walks you through setting up OAuth 2.0 authentication for the GetBill API. OAuth 2.0 provides secure, standardized authorization that allows your application to access GetBill data on behalf of your company.

Why OAuth 2.0?

Security

Your credentials are never stored in the client application. Tokens can be revoked without changing passwords.

Scoped Access

Request only the permissions your application needs. Users can see exactly what access they’re granting.

Standardized

OAuth 2.0 is an industry standard supported by all major platforms and programming languages.

Scalable

Suitable for everything from simple scripts to large enterprise integrations.

Step 1: Create an OAuth Client

Access Your Dashboard

  1. Log in to your GetBill account
  2. Navigate to Company
  3. Find the API Client Management section
  4. Click Create New OAuth Client

Configure Your Client

Save Your Credentials

After creating the client, you’ll receive:
Client ID
string
Public identifier for your application (safe to store in client-side code)
Client Secret
string
Secret key for your application (keep this secure!)
Store your Client Secret securely! Never commit it to version control or expose it in client-side code.

Step 2: Choose Your Grant Type

Client Credentials Grant

Best for: Server-to-server applications, background jobs, automated systems When to use:
  • Your application runs on a secure server
  • No user interaction is required
  • You’re accessing your own company’s data
Flow:
  1. Your application requests a token directly from the authorization server
  2. No user authorization step is required
  3. Token is returned immediately

Authorization Code Grant

Best for: Web applications where users need to authorize access When to use:
  • Multiple users will access the application
  • Users need to authorize what data your app can access
  • You’re building a third-party integration
Flow:
  1. Redirect user to GetBill authorization page
  2. User approves access
  3. GetBill redirects back with authorization code
  4. Exchange authorization code for access token

Step 3: Implementation Examples

Client Credentials Implementation

class GetBillOAuthClient {
  constructor(clientId, clientSecret) {
    this.clientId = clientId;
    this.clientSecret = clientSecret;
    this.accessToken = null;
    this.refreshToken = null;
    this.expiresAt = null;
  }

  async getAccessToken() {
    if (this.accessToken && this.expiresAt > Date.now()) {
      return this.accessToken;
    }

    if (this.refreshToken) {
      return this.refreshAccessToken();
    }

    return this.requestNewToken();
  }

  async requestNewToken() {
    const response = await fetch('https://getbill.io/oauth/token', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/x-www-form-urlencoded',
      },
      body: new URLSearchParams({
        grant_type: 'client_credentials',
        client_id: this.clientId,
        client_secret: this.clientSecret,
        scope: 'debts:read debts:write followups:read company:read'
      })
    });

    if (!response.ok) {
      throw new Error(`Token request failed: ${response.statusText}`);
    }

    const tokenData = await response.json();
    this.updateTokens(tokenData);
    return this.accessToken;
  }

  async refreshAccessToken() {
    const response = await fetch('https://getbill.io/oauth/token', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/x-www-form-urlencoded',
      },
      body: new URLSearchParams({
        grant_type: 'refresh_token',
        refresh_token: this.refreshToken,
        client_id: this.clientId,
        client_secret: this.clientSecret
      })
    });

    if (!response.ok) {
      // Refresh failed, request new token
      return this.requestNewToken();
    }

    const tokenData = await response.json();
    this.updateTokens(tokenData);
    return this.accessToken;
  }

  updateTokens(tokenData) {
    this.accessToken = tokenData.access_token;
    this.refreshToken = tokenData.refresh_token;
    this.expiresAt = Date.now() + (tokenData.expires_in * 1000) - 60000; // 1 min buffer
  }

  async makeAPIRequest(endpoint, options = {}) {
    const token = await this.getAccessToken();
    
    return fetch(`https://getbill.io/external-api/v1${endpoint}`, {
      ...options,
      headers: {
        'Authorization': `Bearer ${token}`,
        'Content-Type': 'application/json',
        ...options.headers
      }
    });
  }
}

// Usage
const client = new GetBillOAuthClient('your_client_id', 'your_client_secret');

// Make API calls
const response = await client.makeAPIRequest('/company/profile');
const profile = await response.json();

Authorization Code Implementation

const express = require('express');
const session = require('express-session');
const { URLSearchParams } = require('url');

const app = express();

app.use(session({
  secret: 'your-session-secret',
  resave: false,
  saveUninitialized: true
}));

const CLIENT_ID = 'your_client_id';
const CLIENT_SECRET = 'your_client_secret';
const REDIRECT_URI = 'http://localhost:3000/oauth/callback';

// Step 1: Redirect to authorization
app.get('/login', (req, res) => {
  const state = Math.random().toString(36).substring(2, 15);
  req.session.oauth_state = state;

  const authURL = new URL('https://getbill.io/oauth/authorize');
  authURL.searchParams.append('response_type', 'code');
  authURL.searchParams.append('client_id', CLIENT_ID);
  authURL.searchParams.append('redirect_uri', REDIRECT_URI);
  authURL.searchParams.append('scope', 'debts:read followups:read company:read');
  authURL.searchParams.append('state', state);

  res.redirect(authURL.toString());
});

// Step 2: Handle callback
app.get('/oauth/callback', async (req, res) => {
  const { code, state } = req.query;

  // Verify state parameter
  if (state !== req.session.oauth_state) {
    return res.status(400).send('Invalid state parameter');
  }

  try {
    // Exchange authorization code for access token
    const tokenResponse = await fetch('https://getbill.io/oauth/token', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/x-www-form-urlencoded',
      },
      body: new URLSearchParams({
        grant_type: 'authorization_code',
        client_id: CLIENT_ID,
        client_secret: CLIENT_SECRET,
        code: code,
        redirect_uri: REDIRECT_URI
      })
    });

    const tokenData = await tokenResponse.json();

    if (!tokenResponse.ok) {
      throw new Error(tokenData.error_description || 'Token exchange failed');
    }

    // Store tokens in session (in production, use a secure database)
    req.session.access_token = tokenData.access_token;
    req.session.refresh_token = tokenData.refresh_token;

    res.redirect('/dashboard');
  } catch (error) {
    console.error('OAuth callback error:', error);
    res.status(500).send('Authentication failed');
  }
});

// Protected route
app.get('/dashboard', async (req, res) => {
  if (!req.session.access_token) {
    return res.redirect('/login');
  }

  try {
    // Make API call with stored token
    const profileResponse = await fetch('https://getbill.io/external-api/v1/company/profile', {
      headers: {
        'Authorization': `Bearer ${req.session.access_token}`
      }
    });

    const profile = await profileResponse.json();
    res.json(profile);
  } catch (error) {
    console.error('API call error:', error);
    res.status(500).send('Failed to fetch profile');
  }
});

app.listen(3000, () => {
  console.log('Server running on http://localhost:3000');
  console.log('Visit http://localhost:3000/login to start OAuth flow');
});

Step 4: Security Best Practices

Secure Token Storage

  • Use environment variables for client secrets
  • Store tokens securely (encrypted database, secure session storage)
  • Never log or expose tokens in error messages

State Parameter

  • Always use the state parameter in authorization code flow
  • Generate a random, unique state for each authorization request
  • Verify the state parameter in your callback

HTTPS Only

  • Always use HTTPS for all OAuth flows
  • Validate SSL certificates
  • Never send tokens over unencrypted connections

Token Management

  • Implement automatic token refresh
  • Handle token expiration gracefully
  • Revoke tokens when no longer needed

Environment Variables Example

# .env file
GETBILL_CLIENT_ID=your_client_id_here
GETBILL_CLIENT_SECRET=your_client_secret_here
GETBILL_REDIRECT_URI=https://yourapp.com/oauth/callback
// Load from environment
const CLIENT_ID = process.env.GETBILL_CLIENT_ID;
const CLIENT_SECRET = process.env.GETBILL_CLIENT_SECRET;
const REDIRECT_URI = process.env.GETBILL_REDIRECT_URI;

Step 5: Testing Your Setup

Test Client Credentials

curl -X POST https://getbill.io/oauth/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=client_credentials" \
  -d "client_id=YOUR_CLIENT_ID" \
  -d "client_secret=YOUR_CLIENT_SECRET" \
  -d "scope=company:read"

Test Authorization Code Flow

  1. Open your browser to the authorization URL
  2. Complete the authorization process
  3. Verify you receive an authorization code
  4. Exchange the code for an access token
  5. Make a test API call

Troubleshooting

Cause: Incorrect client ID or secretSolution:
  • Double-check your client credentials
  • Ensure you’re using the correct environment (test vs production)
  • Verify the client is active in your dashboard
Cause: Requesting scopes not granted to your clientSolution:
  • Check your client configuration in the dashboard
  • Request only the scopes you need
  • Contact support if you need additional scopes
Cause: Invalid authorization code or expired refresh tokenSolution:
  • Ensure the authorization code is used immediately
  • Don’t reuse authorization codes
  • Handle refresh token expiration by re-authenticating
Cause: Redirect URI doesn’t match registered URISolution:
  • Ensure exact match including protocol, domain, and path
  • Register all redirect URIs you plan to use
  • Use localhost for development testing

Production Considerations

Scaling OAuth

  • Token Caching: Cache tokens in Redis or similar for multiple server instances
  • Database Storage: Store refresh tokens securely in your database
  • Rate Limiting: Implement rate limiting for token refresh requests
  • Monitoring: Monitor token usage and refresh patterns

Security Hardening

  • Certificate Pinning: Pin SSL certificates for additional security
  • Token Rotation: Regularly rotate client secrets
  • Audit Logging: Log all OAuth events for security monitoring
  • Scope Minimization: Use the minimum required scopes

Next Steps

Once OAuth is set up:
  1. Make your first API call
  2. Explore the API Reference documentation
  3. Set up webhooks for real-time updates
  4. Review best practices for production use
Need help? Contact our support team at contact@getbill.io