Scripts to synchronize users from various HR systems with TimeCamp. Currently supports:
- BambooHR
- Azure AD / Microsoft Entra ID (via SCIM)
- Clone the repository
- Install dependencies:
pip3 install -r requirements.txt
- Copy
.env.sample
to.env
and fill in your API keys:
cp .env.sample .env
graph TD
BH[1a: BambooHR Fetcher] -->|use| BAPI[BambooHR API]
BH -->|saves| JSON[users.json]
AD[1b: Azure/Entra Fetcher] -->|use| GRAPH[Microsoft Graph API]
AD -->|saves| JSON
TC[2: TimeCamp Synchronizer] -->|uses| JSON
TC -->|use| TAPI[TimeCamp API]
style BH fill:#f9f,stroke:#333
style AD fill:#f9f,stroke:#333
style TC fill:#9f9,stroke:#333
-
Register an application in Azure AD/Entra ID portal:
- Go to Azure Portal > Azure Active Directory > App registrations > New registration
- Name your application (e.g., "TimeCamp SCIM Integration")
- Select "Accounts in this organizational directory only"
- Click Register
- Note down the Application (client) ID and Directory (tenant) ID
-
Create a client secret:
- Go to your app > Certificates & secrets > New client secret
- Give it a description (e.g., "SCIM Integration")
- Select an expiration (e.g., 24 months)
- Click Add
- IMMEDIATELY copy the "Value" column (NOT the Secret ID)
⚠️ The secret value will only be shown once and looks likekv~8Q~...
- If you copied the wrong value or lost it, create a new secret
-
Configure API permissions:
- Go to your app > API permissions
- Click "Add a permission"
- Select "Microsoft Graph" > "Application permissions"
- Add these permissions:
- Directory.Read.All
- User.Read.All
- Group.Read.All
- Click "Grant admin consent" button
-
Configure OAuth credentials in
.env
:
AZURE_TENANT_ID=your-tenant-id # Directory (tenant) ID
AZURE_CLIENT_ID=your-client-id # Application (client) ID
AZURE_CLIENT_SECRET=your-client-secret # The secret value you copied
- (Optional) Configure email preference:
- By default, the script uses the federated ID (userPrincipalName) as the primary email
- To use real email addresses (mail attribute) when available, add this to your
.env
:
AZURE_PREFER_REAL_EMAIL=true
- Fetch users and groups from Azure AD:
python3 azuread_fetch.py
- This script will automatically handle token management and fetch users and groups
- If the token expires or becomes invalid, the script will automatically refresh it
Always test the integration first using these steps:
- Test user fetch from your chosen source:
# For BambooHR:
python bamboohr_fetch.py
# For Azure AD / Microsoft Entra ID:
python azuread_fetch.py
-
Review the generated files to ensure the data is correct:
- For both sources, check
users.json
matches the format shown inusers.json.sample
- For Azure AD, the users.json file will include group memberships in the "groups" field for each user
- For both sources, check
-
Test TimeCamp sync with dry-run:
# Test sync without making any changes
python timecamp_sync.py --dry-run
- If the dry run looks good, run the actual sync:
python timecamp_sync.py
To automate the synchronization, add these entries to your crontab:
# Edit crontab
crontab -e
# Add these lines (choose one source):
# For BambooHR:
# Fetch users from BambooHR every hour
0 * * * * cd /path/to/project && python3 bamboohr_fetch.py
# For Azure AD / Microsoft Entra ID:
# Fetch users and groups every hour (token refresh is handled automatically)
0 * * * * cd /path/to/project && python3 azuread_fetch.py
# Sync with TimeCamp 5 minutes after fetch
5 * * * * cd /path/to/project && python3 timecamp_sync.py
Notes:
- Replace
/path/to/project
with the actual path to your project - Replace
python3
with the path to your Python interpreter (find it usingwhich python3
) - All operations are logged to
logs/sync.log
- Check the logs:
- For real-time output: Watch the script execution in terminal
- For historical data: Check
logs/sync.log
- Ensure all required environment variables are set in
.env
- Verify API keys have the necessary permissions
- For crontab issues, check system logs:
grep CRON /var/log/syslog
- For Azure AD token issues:
- The script automatically handles token refresh when needed
- If you're still having issues, you can manually force a token refresh by deleting the
AZURE_BEARER_TOKEN
line from your.env
file and running the script again
-
User roles
-
TimeCamp groups based on supervisor, not department and division
-
Send email on invite
-
Performance
-
Setting to sync only selected things (like only new users)
-
Setting to move disabled users to specific group_id
-
Remove empty groups
-
Change of email (use external_id to identify)
- User changed name ✅
- User changed group ✅
- User added ✅
- User disabled ✅
- User removed ✅
- User added as inactive ✅
- User added with empty department ✅
- Group name with whitespaces near / ✅
- Setting enabled to add external_id to user name ✅
MIT