Python Examples for FinAutCRM API¶
This section provides Python code examples for interacting with the FinAutCRM API.
Prerequisites¶
Install the requests library:
pip install requests
Credentials Setup¶
Create a client-data.json file in the userdocs/examples/ directory with your credentials:
{
"client_id": "your_client_id_here",
"client_secret": "your_client_secret_here"
}
Warning
Never commit your client-data.json file to version control!
Examples¶
1. Get Access Token¶
01_get_access_token.py - Authenticate and obtain an access token using OAuth2 client credentials flow.
"""
Get an Access Token from the FinAutCRM API.
This script demonstrates how to authenticate with the CRM API using
OAuth2 client credentials flow and obtain an access token.
Prerequisites:
pip install requests
Usage:
python 01_get_access_token.py
"""
import sys, os
import json
from pathlib import Path
# The 'requests' library makes HTTP requests simple and readable.
# Install it with: pip install requests
import requests
# =============================================================================
# Configuration
# =============================================================================
# The base URL for the CRM API
API_BASE_URL_PROD = "https://crm.norsktest.no"
API_BASE_URL_DEV = "http://localhost:8000"
DEV = sys.platform == 'win32' and not os.environ.get('PROD') # Use DEV on Windows unless PROD is set
API_BASE_URL = API_BASE_URL_DEV if DEV else API_BASE_URL_PROD
# Path to the JSON file containing your client credentials.
# This file should contain: {"client_id": "...", "client_secret": "..."}
# IMPORTANT: Never commit this file to version control!
CLIENT_DATA_FILE = Path(__file__).parent.parent / f"client-data-{'dev' if DEV else 'prod'}.json"
# =============================================================================
# Step 1: Load client credentials from file
# =============================================================================
def load_client_credentials(filepath: Path) -> dict:
"""
Load client_id and client_secret from a JSON file.
Storing credentials in a separate file keeps them out of your code
and makes it easier to use different credentials for different environments.
"""
with open(filepath, "r") as f:
return json.load(f)
# =============================================================================
# Step 2: Request an access token
# =============================================================================
def get_access_token(client_id: str, client_secret: str) -> dict:
"""
Exchange client credentials for an access token.
This uses the OAuth2 "client credentials" grant type, which is designed
for machine-to-machine authentication (no user interaction needed).
Args:
client_id: Your application's client ID
client_secret: Your application's client secret
Returns:
The full token response as a dictionary containing:
- access_token: The token to use for API requests
- token_type: Always "Bearer"
- expires_in: Token lifetime in seconds (typically 3600 = 1 hour)
- scope: The permissions granted to this token
"""
# The token endpoint URL
token_url = f"{API_BASE_URL}/api/v1/auth/token"
# The request body - this tells the API what we want
payload = {
"client_id": client_id,
"client_secret": client_secret,
"grant_type": "client_credentials", # This is the OAuth2 flow type
"scope": "read write" # Request both read and write access
}
# The Content-Type header tells the API we're sending JSON
headers = {
"Content-Type": "application/json"
}
# Make the POST request to get a token
response = requests.post(token_url, json=payload, headers=headers)
# Check if the request was successful (status code 200)
# If not, this will raise an exception with details
response.raise_for_status()
# Parse and return the JSON response
return response.json()
# =============================================================================
# Main script
# =============================================================================
def main():
"""Main function that ties everything together.
"""
# Step 1: Load credentials from file
print("Loading client credentials...")
credentials = load_client_credentials(CLIENT_DATA_FILE)
client_id = credentials["client_id"]
client_secret = credentials["client_secret"]
# Show which client we're using (but never print the secret!)
print(f"Client ID: {client_id}")
# Step 2: Get the access token
print("\nRequesting access token...")
token_response = get_access_token(client_id, client_secret)
# Step 3: Display the results
print("\nSuccess! Token response:")
print("-" * 40)
# Pretty-print the response
print(json.dumps(token_response, indent=2))
print("-" * 40)
# Extract the access token for easy copying
access_token = token_response["access_token"]
expires_in = token_response["expires_in"]
print(f"\nYour access token (valid for {expires_in} seconds):")
print(access_token)
print("\nUse this token in the Authorization header for API requests:")
print(f' Authorization: Bearer {access_token[:20]}...')
# This ensures the main() function only runs when the script is executed directly,
# not when it's imported as a module by another script.
if __name__ == "__main__":
main()
2. List Ordninger (Verify Token)¶
02_list_ordninger.py - Fetch ordninger (schemes) to verify your token works. Demonstrates authenticated API requests with pagination.
"""
List Ordninger (Schemes) from the FinAutCRM API.
This script demonstrates how to:
1. Authenticate with the CRM API (get an access token)
2. Make an authenticated API request to fetch data
3. Handle paginated responses
This example builds on 01_get_access_token.py - see that file for
detailed explanations of the authentication process.
Prerequisites:
pip install requests
Usage:
python 02_list_ordninger.py
"""
import json
from pathlib import Path
import requests
# =============================================================================
# Configuration
# =============================================================================
API_BASE_URL = "https://crm.norsktest.no"
CLIENT_DATA_FILE = Path(__file__).parent.parent / "client-data.json"
# =============================================================================
# Authentication (see 01_get_access_token.py for detailed explanation)
# =============================================================================
def load_client_credentials(filepath: Path) -> dict:
"""Load client credentials from JSON file.
"""
with open(filepath, "r") as f:
return json.load(f)
def get_access_token(client_id: str, client_secret: str) -> str:
"""
Get an access token using client credentials.
Returns just the token string (not the full response).
"""
response = requests.post(
f"{API_BASE_URL}/api/v1/auth/token",
json={
"client_id": client_id,
"client_secret": client_secret,
"grant_type": "client_credentials",
"scope": "read write"
},
headers={"Content-Type": "application/json"}
)
response.raise_for_status()
return response.json()["access_token"]
# =============================================================================
# API Request: List Ordninger
# =============================================================================
def list_ordninger(access_token: str, page: int = 1, page_size: int = 20) -> dict:
"""
Fetch a list of ordninger (schemes) from the API.
This demonstrates how to make an authenticated GET request.
The access token must be included in the Authorization header.
Args:
access_token: The OAuth2 access token obtained from authentication
page: Which page of results to fetch (1-based)
page_size: How many items per page (max usually 100)
Returns:
A dictionary containing:
- items: List of ordning objects
- total: Total number of ordninger in the database
- page: Current page number
- page_size: Items per page
- pages: Total number of pages
"""
# The endpoint URL for listing ordninger
url = f"{API_BASE_URL}/api/v1/ordninger/"
# Query parameters for pagination
# These are appended to the URL as ?page=1&page_size=20
params = {
"page": page,
"page_size": page_size
}
# The Authorization header authenticates our request.
# Format: "Bearer <token>" - this is the OAuth2 standard.
headers = {
"Authorization": f"Bearer {access_token}",
"Accept": "application/json" # We want JSON response
}
# Make the GET request
response = requests.get(url, params=params, headers=headers)
# Check for errors (401 = unauthorized, 403 = forbidden, etc.)
response.raise_for_status()
return response.json()
# =============================================================================
# Main script
# =============================================================================
def main():
"""Main function that demonstrates fetching ordninger.
"""
# Step 1: Authenticate
print("Authenticating...")
credentials = load_client_credentials(CLIENT_DATA_FILE)
access_token = get_access_token(
credentials["client_id"],
credentials["client_secret"]
)
print("Authentication successful!\n")
# Step 2: Fetch ordninger
print("Fetching ordninger...")
result = list_ordninger(access_token, page=1, page_size=50)
# import pprint
# pprint.pprint(result)
# Step 3: Display results
total = result["total"]
items = result["items"]
print(f"Found {total} ordninger:\n")
print("-" * 72)
# Display each ordning
# The format string aligns columns for readability
print(f"{'ID':<6} {'Name':<40} {'Short':<10}")
print("-" * 72)
for ordning in items:
# Each ordning has an id, name, and short_name
ordning_id = ordning["name"]
name = ordning["full_name"]
short_name = ordning.get("short_name", "") # .get() handles missing keys
# Truncate long names to fit the column
if len(name) > 38:
name = name[:35] + "..."
print(f"{ordning_id:<6} {name:<40} {short_name:<10}")
print("-" * 60)
print(f"\nShowing {len(items)} of {total} ordninger")
# Show pagination info
print(f"Page {result['page']} of {result['total_pages']}")
if __name__ == "__main__":
main()
3. List Organization Units¶
03_list_orgunits.py - Fetch a paginated list of organization units with optional filtering by kind, active status, or search term.
"""
List Organization Units from the FinAutCRM API.
This script demonstrates how to:
1. Authenticate with the CRM API
2. Fetch a paginated list of organization units
3. Filter orgunits by various criteria (kind, active status, search)
Organization units (orgunits) represent the organizational hierarchy -
companies, departments, teams, etc. Each unit has a type (kind) and
can be filtered by various criteria.
Prerequisites:
pip install requests
Usage:
python 03_list_orgunits.py
"""
import json
from pathlib import Path
import requests
# =============================================================================
# Configuration
# =============================================================================
API_BASE_URL = "https://crm.norsktest.no"
CLIENT_DATA_FILE = Path(__file__).parent.parent / "client-data.json"
# =============================================================================
# Authentication (see 01_get_access_token.py for detailed explanation)
# =============================================================================
def load_client_credentials(filepath: Path) -> dict:
"""Load client credentials from JSON file.
"""
with open(filepath, "r") as f:
return json.load(f)
def get_access_token(client_id: str, client_secret: str) -> str:
"""
Get an access token using client credentials.
Returns just the token string (not the full response).
"""
response = requests.post(
f"{API_BASE_URL}/api/v1/auth/token",
json={
"client_id": client_id,
"client_secret": client_secret,
"grant_type": "client_credentials",
"scope": "read write"
},
headers={"Content-Type": "application/json"}
)
response.raise_for_status()
return response.json()["access_token"]
# =============================================================================
# API Request: List Organization Units
# =============================================================================
def list_orgunits(
access_token: str,
page: int = 1,
page_size: int = 20,
kind: str = None,
active: bool = None,
search: str = None
) -> dict:
"""
Fetch a paginated list of organization units from the API.
Each organization unit has:
- id: Unique identifier
- name: Display name of the unit
- kind: Type of unit (e.g., 'company', 'department', 'team')
- active: Whether the unit is currently active
- depth: How deep in the tree (1 = root, 2 = child of root, etc.)
- numchild: Number of direct children this unit has
Args:
access_token: The OAuth2 access token
page: Which page of results to fetch (1-based)
page_size: How many items per page
kind: Filter by organization type (e.g., 'company', 'department')
active: Filter by active status (True/False/None for all)
search: Search in name and email fields
Returns:
A dictionary containing paginated results with 'items', 'total',
'page', 'page_size', and 'total_pages'.
"""
url = f"{API_BASE_URL}/api/v1/orgunits"
# Query parameters for pagination and filtering
params = {
"page": page,
"page_size": page_size
}
# Add optional filters only if provided
if kind is not None:
params["kind"] = kind
if active is not None:
params["active"] = str(active).lower() # API expects 'true'/'false'
if search is not None:
params["search"] = search
# Authorization header with Bearer token
headers = {
"Authorization": f"Bearer {access_token}",
"Accept": "application/json"
}
response = requests.get(url, params=params, headers=headers)
response.raise_for_status()
return response.json()
# =============================================================================
# Display Helper
# =============================================================================
def display_orgunit_list(orgunits: list):
"""
Display a list of organization units in a table format.
The 'depth' field indicates the hierarchy level:
- depth=1: Root level (top of hierarchy)
- depth=2: Child of root
- etc.
"""
print(f"{'ID':<6} {'Name':<40} {'Kind':<12} {'Active':<8} {'Depth'}")
print("-" * 80)
for unit in orgunits:
name = unit["name"]
if len(name) > 38:
name = name[:35] + "..."
active_str = "Yes" if unit["active"] else "No"
kind = unit.get("kind", "")
depth = unit.get("depth", 1)
print(f"{unit['id']:<6} {name:<40} {kind:<12} {active_str:<8} {depth}")
# =============================================================================
# Main script
# =============================================================================
def main():
"""Main function demonstrating organization unit listing.
"""
# Step 1: Authenticate
print("Authenticating...")
credentials = load_client_credentials(CLIENT_DATA_FILE)
access_token = get_access_token(
credentials["client_id"],
credentials["client_secret"]
)
print("Authentication successful!\n")
# Step 2: List all organization units
print("Fetching organization units...\n")
result = list_orgunits(access_token, page=1, page_size=25)
# Display summary and table
total = result["total"]
items = result["items"]
print(f"Found {total} organization units\n")
display_orgunit_list(items)
print("-" * 80)
print(f"\nShowing {len(items)} of {total} orgunits")
print(f"Page {result['page']} of {result['total_pages']}")
# Step 3: Demonstrate filtering (optional)
# Uncomment to filter by active units only:
#
# print("\n\nFiltering active units only...")
# active_result = list_orgunits(access_token, active=True)
# print(f"Found {active_result['total']} active units")
#
# Or search by name:
# search_result = list_orgunits(access_token, search="Sales")
# print(f"Found {search_result['total']} units matching 'Sales'")
if __name__ == "__main__":
main()
4. Get Organization Unit Tree¶
04_get_orgunit_tree.py - Fetch a single organization unit with its full tree of descendants. Demonstrates hierarchical data traversal.
"""
Get Organization Unit Tree from the FinAutCRM API.
This script demonstrates how to:
1. Authenticate with the CRM API
2. Fetch a single organization unit with its full tree of descendants
3. Display a hierarchical tree structure
Organization units form a tree hierarchy. When you fetch a single unit
by ID, the API returns that unit along with all its descendants nested
in a 'children' array, allowing you to display the complete subtree.
Prerequisites:
pip install requests
Usage:
python 04_get_orgunit_tree.py
"""
import json
from pathlib import Path
import requests
# =============================================================================
# Configuration
# =============================================================================
# API_BASE_URL = "https://crm.norsktest.no"
API_BASE_URL = "http://localhost:8000"
CLIENT_DATA_FILE = Path(__file__).parent.parent / "client-data.json"
# The ID of the organization unit to fetch
# Change this to explore different parts of the hierarchy
ORGUNIT_ID = 169 # Example: root organization
# ORGUNIT_ID = 528 # Example: root organization
# =============================================================================
# Authentication (see 01_get_access_token.py for detailed explanation)
# =============================================================================
def load_client_credentials(filepath: Path) -> dict:
"""Load client credentials from JSON file.
"""
with open(filepath, "r") as f:
return json.load(f)
def get_access_token(client_id: str, client_secret: str) -> str:
"""
Get an access token using client credentials.
Returns just the token string (not the full response).
"""
response = requests.post(
f"{API_BASE_URL}/api/v1/auth/token",
json={
"client_id": client_id,
"client_secret": client_secret,
"grant_type": "client_credentials",
"scope": "read write"
},
headers={"Content-Type": "application/json"}
)
response.raise_for_status()
return response.json()["access_token"]
# =============================================================================
# API Request: Get Organization Unit Tree
# =============================================================================
def get_orgunit_tree(access_token: str, orgunit_id: int) -> dict:
"""
Fetch a single organization unit with its full tree of descendants.
This endpoint returns the requested unit along with all its children,
grandchildren, etc., nested in a 'children' array. This allows you to
display or process the complete organizational hierarchy below any node.
Args:
access_token: The OAuth2 access token
orgunit_id: The ID of the organization unit to fetch
Returns:
The organization unit with nested 'children' containing all descendants.
Each unit includes: id, name, kind, active, depth, and children array.
"""
url = f"{API_BASE_URL}/api/v1/orgunits/{orgunit_id}"
headers = {
"Authorization": f"Bearer {access_token}",
"Accept": "application/json"
}
response = requests.get(url, headers=headers)
response.raise_for_status()
# print response header for debugging (one on each line)
for key, value in response.headers.items():
print(f"{key}: {value}")
return response.json()
# =============================================================================
# Display Helper
# =============================================================================
def display_tree(unit: dict, prefix: str = "", is_last: bool = True):
"""
Recursively display an organization unit tree with visual connectors.
Uses box-drawing characters to create a tree visualization:
├── for middle children
└── for the last child
│ for vertical continuation
Args:
unit: The organization unit dictionary
prefix: Current line prefix for indentation
is_last: Whether this is the last child of its parent
"""
# Choose the connector based on position
connector = "└── " if is_last else "├── "
# Format unit info
name = unit["name"]
kind = unit.get("kind", "")
active = "✓" if unit["active"] else "✗"
unit_id = unit["id"]
# Print this unit (root has no connector)
if prefix == "":
print(f"{name} ({kind}) [active: {active}] - ID: {unit_id}")
else:
print(f"{prefix}{connector}{name} ({kind}) [{active}] ID: {unit_id}")
# Prepare prefix for children
# If this is the last child, don't draw a vertical line below
child_prefix = prefix + (" " if is_last else "│ ")
# Recursively display children
children = unit.get("children", [])
for i, child in enumerate(children):
is_last_child = (i == len(children) - 1)
display_tree(child, child_prefix, is_last_child)
def count_nodes(unit: dict) -> int:
"""Count total nodes in a tree (including the root).
"""
count = 1
for child in unit.get("children", []):
count += count_nodes(child)
return count
# =============================================================================
# Main script
# =============================================================================
def main():
"""Main function demonstrating organization unit tree retrieval.
"""
# Step 1: Authenticate
print("Authenticating...")
credentials = load_client_credentials(CLIENT_DATA_FILE)
access_token = get_access_token(
credentials["client_id"],
credentials["client_secret"]
)
print("Authentication successful!\n")
# Step 2: Fetch the organization unit tree
print(f"Fetching organization unit tree for ID {ORGUNIT_ID}...\n")
tree = get_orgunit_tree(access_token, ORGUNIT_ID)
# Step 3: Display tree info
total_nodes = count_nodes(tree)
print(f"Tree contains {total_nodes} organization units\n")
print("=" * 70)
# Step 4: Display the tree
display_tree(tree)
print("=" * 70)
print(f"\nTotal units in tree: {total_nodes}")
if __name__ == "__main__":
main()