Authentication Troubleshooting

Resolve common authentication errors and debug integration issues with LendFoundry APIs


title: "Authentication Troubleshooting" slug: "authentication-troubleshooting" excerpt: "Resolve common authentication errors and debug integration issues with LendFoundry APIs" category: "API Reference" hidden: false order: 3

Authentication Troubleshooting

📘

Quick Fixes for Common Issues

This guide helps you diagnose and resolve authentication errors quickly. Most issues stem from using the wrong authentication method for each system.


Error Quick Reference

%%{init: {'theme': 'base', 'themeVariables': {'background': '#ffffff', 'mainBkg': '#ffffff', 'clusterBkg': '#f9fafb', 'clusterBorder': '#2563eb', 'primaryColor': '#2563eb', 'primaryTextColor': '#ffffff', 'titleColor': '#000000', 'lineColor': '#374151'}, 'flowchart': {'padding': 25, 'nodeSpacing': 45, 'rankSpacing': 60}}}%%
flowchart TB
    subgraph DIAG[🔍 Authentication Error Diagnosis]
        A[❌ 401 Error] --> B{Which System?}
        B -->|LOS| C{Bearer Prefix?}
        B -->|LMS| D{Token Expired?}
        C -->|Yes| E[❌ Remove Bearer]
        C -->|No| F[🔑 Check API Key]
        D -->|Yes| G[🔄 Refresh Token]
        D -->|No| H[🔑 Check Format]
    end
    
    style A fill:#dc2626,stroke:#b91c1c,color:#fff
    style B fill:#6b7280,stroke:#4b5563,color:#fff
    style C fill:#6b7280,stroke:#4b5563,color:#fff
    style D fill:#6b7280,stroke:#4b5563,color:#fff
    style E fill:#059669,stroke:#047857,color:#fff
    style F fill:#d97706,stroke:#b45309,color:#fff
    style G fill:#059669,stroke:#047857,color:#fff
    style H fill:#d97706,stroke:#b45309,color:#fff
HTTP StatusError CodeSystemCommon CauseSolution
401UNAUTHORIZEDBothInvalid/missing credentialsVerify credentials
401TOKEN_EXPIREDLMSJWT token expiredRefresh token
403FORBIDDENBothInsufficient permissionsContact support
429RATE_LIMIT_EXCEEDEDBothToo many requestsImplement backoff

Application Submission APIs (LOS) Issues

Error: 401 Unauthorized

Symptoms:

{
  "error": {
    "code": "UNAUTHORIZED",
    "message": "Invalid or missing API key"
  }
}

Diagnostic Checklist:

1. Check for Bearer Prefix (Most Common Issue)

# ❌ WRONG - Has "Bearer" prefix
Authorization: Bearer abc123def456

# ✅ CORRECT - No prefix
Authorization: abc123def456

2. Verify API Key Format

import os

api_key = os.getenv('LENDFOUNDRY_LOS_API_KEY')

# Check for common issues
if api_key is None:
    print("❌ API key not set in environment")
elif api_key.startswith('Bearer '):
    print("❌ API key contains 'Bearer ' prefix - remove it")
elif ' ' in api_key:
    print("❌ API key contains spaces - check for copy/paste errors")
elif len(api_key) < 10:
    print("⚠️ API key seems too short - verify correct key")
else:
    print("✅ API key format looks correct")

3. Verify Environment

EnvironmentBase URL
Demo/Sandboxhttps://loc.demo.kendra.lendfoundry.com/v1/darbaan
ProductionContact support for production URL
⚠️

Important

Demo API keys do not work in production, and vice versa.

4. Test Authentication

curl -v -X POST "https://loc.demo.kendra.lendfoundry.com/v1/darbaan/back-office/rest/api/list-applications" \
  -H "Authorization: your-api-key-here" \
  -H "Content-Type: application/json" \
  -d '{"page": 1, "size": 1}'

Expected: 200 OK or 401 Unauthorized with clear error message


Error: 403 Forbidden

Symptoms:

{
  "error": {
    "code": "FORBIDDEN",
    "message": "Insufficient permissions",
    "details": "Your API key does not have permission to access this resource"
  }
}

Causes:

  1. API key doesn't have required permissions for the endpoint
  2. Using sandbox credentials with production endpoint
  3. Organization role restrictions

Solution:

  1. Contact [email protected] to verify permissions
  2. Confirm you're using the correct environment credentials
  3. Check if your organization has access to the requested endpoint

Active Loan Servicing APIs (LMS) Issues

Error: 401 Unauthorized - Token Expired

Symptoms:

{
  "error": {
    "code": "TOKEN_EXPIRED",
    "message": "JWT token has expired",
    "details": "Token expired at 2025-12-15T10:30:00Z. Request a new token."
  }
}

Cause: JWT tokens expire after 1 hour (3600 seconds)

Solution: Implement automatic token refresh with buffer time:

from datetime import datetime, timedelta
import requests
import os

class TokenManager:
    """Manage JWT tokens with automatic refresh."""
    
    def __init__(self):
        self.client_id = os.getenv('LENDFOUNDRY_LMS_CLIENT_ID')
        self.client_secret = os.getenv('LENDFOUNDRY_LMS_CLIENT_SECRET')
        self.base_url = "https://api.demo.lms.lendfoundry.com"
        self.token = None
        self.expires_at = None
    
    def get_valid_token(self):
        """Get valid token, refreshing if needed."""
        # Refresh 5 minutes before expiry
        if self.token and self.expires_at:
            if datetime.now() < self.expires_at - timedelta(minutes=5):
                return self.token
        
        return self._refresh_token()
    
    def _refresh_token(self):
        """Request new token from OAuth endpoint."""
        response = requests.post(
            f"{self.base_url}/oauth/token",
            data={
                "grant_type": "client_credentials",
                "client_id": self.client_id,
                "client_secret": self.client_secret
            }
        )
        response.raise_for_status()
        
        data = response.json()
        self.token = data["access_token"]
        self.expires_at = datetime.now() + timedelta(seconds=data.get("expires_in", 3600))
        
        return self.token

# Usage - always use get_valid_token() before requests
token_manager = TokenManager()
token = token_manager.get_valid_token()

Error: 401 Unauthorized - Invalid Token Format

Symptoms:

{
  "error": {
    "code": "UNAUTHORIZED",
    "message": "Invalid token format",
    "details": "Bearer token is malformed"
  }
}

Common Mistakes:

❌ WrongIssue
Authorization: eyJhbG...Missing Bearer prefix
Authorization: Bearer eyJhbG...Double space after Bearer
Authorization: bearer eyJhbG...Lowercase bearer
Authorization: Bearer abc123Using API key instead of JWT

Correct Format:

Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

Validation Code:

def validate_token_format(token):
    """Validate JWT token format."""
    import jwt
    
    # Check if it looks like a JWT (3 parts separated by dots)
    parts = token.split('.')
    if len(parts) != 3:
        print("❌ Invalid JWT format - should have 3 parts")
        return False
    
    # Decode without verification to check structure
    try:
        decoded = jwt.decode(token, options={"verify_signature": False})
        print(f"✅ Valid JWT structure")
        print(f"   Expires: {datetime.fromtimestamp(decoded.get('exp', 0))}")
        return True
    except Exception as e:
        print(f"❌ Invalid JWT: {e}")
        return False

Error: 401 Unauthorized - Invalid Client Credentials

Symptoms:

{
  "error": {
    "code": "INVALID_CLIENT",
    "message": "Invalid client_id or client_secret"
  }
}

Diagnostic Checklist:

  1. ✅ Verify client_id is correct (copy-paste, don't type)
  2. ✅ Verify client_secret is correct
  3. ✅ Check for leading/trailing whitespace
  4. ✅ Confirm credentials are for correct environment
  5. ✅ Check if credentials have been regenerated
import os

client_id = os.getenv('LENDFOUNDRY_LMS_CLIENT_ID')
client_secret = os.getenv('LENDFOUNDRY_LMS_CLIENT_SECRET')

# Remove any accidental whitespace
client_id = client_id.strip() if client_id else None
client_secret = client_secret.strip() if client_secret else None

# Validate
if not client_id:
    print("❌ CLIENT_ID not set")
elif not client_secret:
    print("❌ CLIENT_SECRET not set")
else:
    print(f"✅ Credentials loaded")
    print(f"   Client ID length: {len(client_id)}")
    print(f"   Client Secret length: {len(client_secret)}")

Common Integration Mistakes

Mistake 1: Using Wrong Authentication Method

Symptom: 401 errors when switching between LOS and LMS

%%{init: {'theme': 'base', 'themeVariables': {'background': '#ffffff', 'mainBkg': '#ffffff', 'clusterBkg': '#f9fafb', 'clusterBorder': '#2563eb', 'primaryColor': '#2563eb', 'primaryTextColor': '#ffffff', 'titleColor': '#000000', 'lineColor': '#374151'}, 'flowchart': {'padding': 25, 'nodeSpacing': 45, 'rankSpacing': 60}}}%%
flowchart LR
    subgraph WRONG[❌ Common Mistake]
        A1[LOS API Key] --> B1[LMS Endpoint]
        B1 --> C1[401 Error]
    end
    
    subgraph RIGHT[✅ Correct Approach]
        A2[LOS API Key] --> B2[LOS Endpoint]
        A3[LMS JWT] --> B3[LMS Endpoint]
        B2 --> C2[200 OK]
        B3 --> C3[200 OK]
    end
    
    style A1 fill:#dc2626,stroke:#b91c1c,color:#fff
    style B1 fill:#dc2626,stroke:#b91c1c,color:#fff
    style C1 fill:#dc2626,stroke:#b91c1c,color:#fff
    style A2 fill:#059669,stroke:#047857,color:#fff
    style A3 fill:#059669,stroke:#047857,color:#fff
    style B2 fill:#059669,stroke:#047857,color:#fff
    style B3 fill:#059669,stroke:#047857,color:#fff
    style C2 fill:#059669,stroke:#047857,color:#fff
    style C3 fill:#059669,stroke:#047857,color:#fff

Solution: Create separate clients for each system:

class LendFoundryClient:
    """Unified client for LOS and LMS with correct authentication."""
    
    def __init__(self, los_api_key, lms_client_id, lms_client_secret):
        self.los_api_key = los_api_key
        self.lms_token_manager = TokenManager(lms_client_id, lms_client_secret)
    
    def call_los(self, endpoint, data):
        """Call Application Submission API (LOS)."""
        headers = {
            "Authorization": self.los_api_key,  # No Bearer prefix
            "Content-Type": "application/json"
        }
        return requests.post(
            f"https://loc.demo.kendra.lendfoundry.com/v1/darbaan{endpoint}",
            json=data,
            headers=headers
        )
    
    def call_lms(self, endpoint, method="GET", data=None):
        """Call Active Loan Servicing API (LMS)."""
        token = self.lms_token_manager.get_valid_token()
        headers = {
            "Authorization": f"Bearer {token}",  # Bearer prefix required
            "Content-Type": "application/json"
        }
        return requests.request(
            method,
            f"https://api.demo.lms.lendfoundry.com/v1{endpoint}",
            json=data,
            headers=headers
        )

Mistake 2: Not Handling Token Expiration

Symptom: Works initially, then starts failing after 1 hour

Solution: Implement middleware that always ensures valid token:

class LMSApiClient:
    """LMS API client with automatic token refresh."""
    
    def __init__(self, client_id, client_secret):
        self.token_manager = TokenManager(client_id, client_secret)
    
    def request(self, method, endpoint, **kwargs):
        """Make request with automatic token refresh."""
        token = self.token_manager.get_valid_token()
        
        headers = kwargs.pop('headers', {})
        headers['Authorization'] = f'Bearer {token}'
        headers['Content-Type'] = 'application/json'
        
        response = requests.request(
            method,
            f"https://api.demo.lms.lendfoundry.com/v1{endpoint}",
            headers=headers,
            **kwargs
        )
        
        # If token expired mid-request, refresh and retry once
        if response.status_code == 401:
            error_data = response.json()
            if error_data.get('error', {}).get('code') == 'TOKEN_EXPIRED':
                token = self.token_manager._refresh_token()
                headers['Authorization'] = f'Bearer {token}'
                response = requests.request(
                    method,
                    f"https://api.demo.lms.lendfoundry.com/v1{endpoint}",
                    headers=headers,
                    **kwargs
                )
        
        return response

Mistake 3: Hardcoding Credentials

Risk: Credentials in version control, security exposure

# ❌ NEVER DO THIS
API_KEY = "abc123def456"  # Hardcoded credentials

# ✅ USE ENVIRONMENT VARIABLES
import os
API_KEY = os.getenv('LENDFOUNDRY_LOS_API_KEY')

# ✅ OR USE SECRETS MANAGER
import boto3

def get_secret(secret_name):
    client = boto3.client('secretsmanager')
    response = client.get_secret_value(SecretId=secret_name)
    return response['SecretString']

API_KEY = get_secret('lendfoundry/los-api-key')

Debugging Tips

Enable Request Logging

import logging
import requests
from http.client import HTTPConnection

# Enable debug logging
logging.basicConfig(level=logging.DEBUG)
HTTPConnection.debuglevel = 1

# Now requests will show full HTTP details
response = requests.post(url, headers=headers, json=data)

Decode JWT Token for Debugging

import jwt
from datetime import datetime

def debug_jwt_token(token):
    """Decode and display JWT token information."""
    try:
        # Decode without verification (for debugging only)
        decoded = jwt.decode(token, options={"verify_signature": False})
        
        print("=== JWT Token Debug ===")
        print(f"Subject: {decoded.get('sub', 'N/A')}")
        print(f"Issued At: {datetime.fromtimestamp(decoded.get('iat', 0))}")
        print(f"Expires At: {datetime.fromtimestamp(decoded.get('exp', 0))}")
        
        now = datetime.now()
        exp = datetime.fromtimestamp(decoded.get('exp', 0))
        
        if now > exp:
            print(f"⚠️ TOKEN EXPIRED {now - exp} ago")
        else:
            print(f"✅ Token valid for {exp - now}")
        
        return decoded
    except Exception as e:
        print(f"❌ Failed to decode token: {e}")
        return None

# Usage
debug_jwt_token(your_jwt_token)

cURL Verbose Mode

curl -v -X POST "https://loc.demo.kendra.lendfoundry.com/v1/darbaan/back-office/rest/api/list-applications" \
  -H "Authorization: your-api-key" \
  -H "Content-Type: application/json" \
  -d '{"page": 1, "size": 1}'

The -v flag shows:

  • Request headers sent
  • Response headers received
  • Connection details
  • SSL/TLS information

Getting Help

Before Contacting Support

  1. ✅ Review this troubleshooting guide
  2. ✅ Verify credentials are correct (copy-paste, no typos)
  3. ✅ Test with cURL to isolate code issues
  4. ✅ Check error response body for details
  5. ✅ Confirm using correct environment (sandbox vs production)

When Contacting Support

Provide:

InformationExample
Error messageFull JSON response
HTTP status code401, 403, etc.
Endpoint/back-office/rest/api/list-applications
EnvironmentSandbox / Production
SystemLOS or LMS
Code snippetSanitized (no credentials!)

Contact:

  • Email: [email protected]
  • Subject: "Authentication Issue - [LOS/LMS] - [Brief Description]"

Next Steps

ResourceDescription
AuthenticationAuthentication setup guide
API Integration FlowComplete integration workflow
Error CodesFull error code reference

Issue Resolved?

Head to the API Integration Flow to continue your integration.