Authentication

Learn how to authenticate with LendFoundry APIs using API Keys and Bearer JWT tokens

Authentication

📘

Two Authentication Methods

Application Submission APIs use API Key authentication. Active Loan Servicing APIs use Bearer JWT authentication. Using the wrong method is the most common integration error.

LendFoundry uses different authentication methods for each system to provide optimal security for different use cases.


Authentication 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 LR
    subgraph AUTH[🔐 Authentication Methods]
        direction TB
        A[🖥️ Your App] --> B{Which System?}
        B -->|LOS| C[🔑 API Key]
        B -->|LMS| D[🎫 Bearer JWT]
    end
    
    style A fill:#2563eb,stroke:#1e40af,color:#fff
    style B fill:#6b7280,stroke:#4b5563,color:#fff
    style C fill:#059669,stroke:#047857,color:#fff
    style D fill:#059669,stroke:#047857,color:#fff
SystemHeader FormatExample
Application Submission APIsAuthorization: TOKENAuthorization: abc123def456
Active Loan Servicing APIsAuthorization: Bearer TOKENAuthorization: Bearer eyJhbG...
⚠️

Critical Difference

  • Application Submission APIs: No Bearer prefix
  • Active Loan Servicing APIs: Requires Bearer prefix

Application Submission APIs Authentication

Application Submission APIs use simple API Key authentication. The API key is passed directly in the Authorization header without any prefix.

Header Format

Authorization: your-api-key-here

Making Authenticated Requests

curl -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": 10}'
const listApplications = async (apiKey) => {
  const response = await fetch(
    'https://loc.demo.kendra.lendfoundry.com/v1/darbaan/back-office/rest/api/list-applications',
    {
      method: 'POST',
      headers: {
        'Authorization': apiKey,  // No Bearer prefix
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({ page: 1, size: 10 })
    }
  );
  
  if (!response.ok) {
    throw new Error(`HTTP error! status: ${response.status}`);
  }
  
  return await response.json();
};

// Usage
const apiKey = process.env.LENDFOUNDRY_LOS_API_KEY;
const applications = await listApplications(apiKey);
import requests
import os

def list_applications(api_key):
    """List loan applications using LOS API."""
    url = "https://loc.demo.kendra.lendfoundry.com/v1/darbaan/back-office/rest/api/list-applications"
    headers = {
        "Authorization": api_key,  # No Bearer prefix
        "Content-Type": "application/json"
    }
    
    response = requests.post(url, json={"page": 1, "size": 10}, headers=headers)
    response.raise_for_status()
    return response.json()

# Usage
api_key = os.getenv('LENDFOUNDRY_LOS_API_KEY')
applications = list_applications(api_key)
public class LOSClient {
    private final String apiKey;
    private final HttpClient client;
    
    public LOSClient(String apiKey) {
        this.apiKey = apiKey;
        this.client = HttpClient.newHttpClient();
    }
    
    public String listApplications() throws Exception {
        HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create("https://loc.demo.kendra.lendfoundry.com/v1/darbaan/back-office/rest/api/list-applications"))
            .header("Authorization", apiKey)  // No Bearer prefix
            .header("Content-Type", "application/json")
            .POST(HttpRequest.BodyPublishers.ofString("{\"page\": 1, \"size\": 10}"))
            .build();
        
        HttpResponse<String> response = client.send(request, 
            HttpResponse.BodyHandlers.ofString());
        return response.body();
    }
}

// Usage
String apiKey = System.getenv("LENDFOUNDRY_LOS_API_KEY");
LOSClient client = new LOSClient(apiKey);
String applications = client.listApplications();

Active Loan Servicing APIs Authentication

Active Loan Servicing APIs use Bearer JWT authentication. You must include the Bearer prefix before your JWT token.

Header Format

Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

Token Lifecycle

%%{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 TOKEN[🎫 JWT Token Lifecycle]
        A[🔑 Request] --> B[🎫 Receive]
        B --> C[📋 Use]
        C --> D{⏰ Expired?}
        D -->|No| C
        D -->|Yes| A
    end
    
    style A fill:#2563eb,stroke:#1e40af,color:#fff
    style B fill:#059669,stroke:#047857,color:#fff
    style C fill:#2563eb,stroke:#1e40af,color:#fff
    style D fill:#6b7280,stroke:#4b5563,color:#fff
PropertyValue
Token TypeJWT (JSON Web Token)
Expiration1 hour (3600 seconds)
Refresh StrategyRequest new token before expiry

Making Authenticated Requests

curl -X GET "https://api.demo.lms.lendfoundry.com/v1/loan-management/search/LN-2024-001234/details" \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
  -H "Content-Type: application/json"
const getLoanDetails = async (loanNumber, jwtToken) => {
  const response = await fetch(
    `https://api.demo.lms.lendfoundry.com/v1/loan-management/search/${loanNumber}/details`,
    {
      method: 'GET',
      headers: {
        'Authorization': `Bearer ${jwtToken}`,  // Bearer prefix required
        'Content-Type': 'application/json'
      }
    }
  );
  
  if (!response.ok) {
    throw new Error(`HTTP error! status: ${response.status}`);
  }
  
  return await response.json();
};

// Usage
const jwtToken = await getValidToken();
const loan = await getLoanDetails('LN-2024-001234', jwtToken);
import requests
import os

def get_loan_details(loan_number, jwt_token):
    """Get loan details from LMS API."""
    url = f"https://api.demo.lms.lendfoundry.com/v1/loan-management/search/{loan_number}/details"
    headers = {
        "Authorization": f"Bearer {jwt_token}",  # Bearer prefix required
        "Content-Type": "application/json"
    }
    
    response = requests.get(url, headers=headers)
    response.raise_for_status()
    return response.json()

# Usage
jwt_token = get_valid_token()
loan = get_loan_details('LN-2024-001234', jwt_token)
public class LMSClient {
    private final String jwtToken;
    private final HttpClient client;
    
    public LMSClient(String jwtToken) {
        this.jwtToken = jwtToken;
        this.client = HttpClient.newHttpClient();
    }
    
    public String getLoanDetails(String loanNumber) throws Exception {
        HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create("https://api.demo.lms.lendfoundry.com/v1/loan-management/search/" + loanNumber + "/details"))
            .header("Authorization", "Bearer " + jwtToken)  // Bearer prefix required
            .header("Content-Type", "application/json")
            .GET()
            .build();
        
        HttpResponse<String> response = client.send(request, 
            HttpResponse.BodyHandlers.ofString());
        return response.body();
    }
}

// Usage
String jwtToken = getValidToken();
LMSClient client = new LMSClient(jwtToken);
String loanDetails = client.getLoanDetails("LN-2024-001234");

Token Management

Implementing Token Refresh

Since JWT tokens expire after 1 hour, implement proactive token refresh:

from datetime import datetime, timedelta
import requests

class TokenManager:
    """Manage JWT token lifecycle with automatic refresh."""
    
    def __init__(self, client_id, client_secret, base_url):
        self.client_id = client_id
        self.client_secret = client_secret
        self.base_url = base_url
        self.token = None
        self.token_expires_at = None
    
    def get_valid_token(self):
        """Get a valid token, refreshing if necessary."""
        # Check if token exists and is valid (with 5 min buffer)
        if self.token and self.token_expires_at:
            buffer_time = self.token_expires_at - timedelta(minutes=5)
            if datetime.now() < buffer_time:
                return self.token
        
        # Token expired or doesn't exist - get new one
        return self._refresh_token()
    
    def _refresh_token(self):
        """Request a new token from the 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()
        
        token_data = response.json()
        self.token = token_data["access_token"]
        expires_in = token_data.get("expires_in", 3600)
        self.token_expires_at = datetime.now() + timedelta(seconds=expires_in)
        
        return self.token

# Usage
token_manager = TokenManager(
    client_id=os.getenv('LENDFOUNDRY_LMS_CLIENT_ID'),
    client_secret=os.getenv('LENDFOUNDRY_LMS_CLIENT_SECRET'),
    base_url="https://api.demo.lms.lendfoundry.com"
)

# Always get a valid token before making requests
token = token_manager.get_valid_token()

Common Mistakes

❌ Wrong✅ CorrectError
LOS with Bearer: Authorization: Bearer abc123LOS: Authorization: abc123401 Unauthorized
LMS without Bearer: Authorization: eyJhbG...LMS: Authorization: Bearer eyJhbG...401 Unauthorized
Expired JWT tokenRefresh token before expiry401 Token Expired
Extra spaces: Bearer eyJhbG...Single space: Bearer eyJhbG...401 Invalid Token

Error Responses

401 Unauthorized

Cause: Invalid or missing credentials

{
  "error": {
    "code": "UNAUTHORIZED",
    "message": "Invalid or missing authentication credentials",
    "trace_id": "a1b2c3d4e5"
  }
}

Resolution:

  • Verify API key or JWT token is correct
  • Check header format matches system requirements
  • Ensure token has not expired (LMS only)

403 Forbidden

Cause: Insufficient permissions

{
  "error": {
    "code": "FORBIDDEN",
    "message": "Insufficient permissions to access this resource",
    "trace_id": "a1b2c3d4e5"
  }
}

Resolution:

  • Contact support to verify account permissions
  • Check if using correct environment credentials

Security Best Practices

✅ DO

PracticeExample
Use environment variablesos.getenv('LENDFOUNDRY_API_KEY')
Implement token refreshRefresh JWT 5 minutes before expiry
Use HTTPS onlyAll LendFoundry APIs require HTTPS
Store credentials securelyUse secrets management (AWS Secrets Manager, HashiCorp Vault)

❌ DON'T

Anti-PatternRisk
Hardcode credentials in codeCredentials in version control
Share API keysUnauthorized access
Ignore token expirationFailed requests after 1 hour
Log full tokensSecurity exposure in logs

Secure Credential Storage

import os

# ✅ Good - Environment variables
api_key = os.getenv('LENDFOUNDRY_LOS_API_KEY')
client_id = os.getenv('LENDFOUNDRY_LMS_CLIENT_ID')
client_secret = os.getenv('LENDFOUNDRY_LMS_CLIENT_SECRET')

# ✅ Better - Secrets manager (AWS example)
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')

Getting API Credentials

Request Process

  1. Contact Sales: Email [email protected]
  2. Specify Requirements:
    • Company name
    • System(s) needed (Application Submission, Active Loan Servicing, or both)
    • Environment (sandbox/production)
  3. Receive Credentials: Securely delivered through your onboarding process
  4. Store Securely: Use environment variables or secrets management

Test Your Authentication

Test Application Submission APIs

curl -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}'

Expected Response: 200 OK with application list

Test Active Loan Servicing APIs

curl -X GET "https://api.demo.lms.lendfoundry.com/v1/loan-management/search/TEST123/details" \
  -H "Authorization: Bearer YOUR_JWT_TOKEN" \
  -H "Content-Type: application/json"

Expected Response: 200 OK with loan details or 404 Not Found if loan doesn't exist


Next Steps

ResourceDescription
Authentication TroubleshootingResolve common authentication issues
API Integration FlowUnderstand the complete integration workflow
Rate LimitingLearn about rate limits and best practices

Authentication Setup Complete?

Head to the API Integration Flow to understand how to integrate both systems.