API Integration with System Accounts and Impersonation
Complete guide for developers integrating MangoApps APIs into their applications using system accounts and impersonation. This approach allows you to exercise all APIs on behalf of different users while maintaining proper security and audit trails.
Target Audience: Developers building integrations with MangoApps APIs
Prerequisites: Admin access to create service account tokens
API Version: v1
Overview
What Are System Accounts and Impersonation?
System Accounts (Service Accounts) are special API tokens designed for system-to-system integrations. They provide:
- Two-factor authentication (token + secret)
- Permission scopes to limit access
- Audit trails for all API calls
- Ability to impersonate other users
Impersonation allows a system account to create temporary tokens that act on behalf of specific users. This enables:
- Testing APIs with different user contexts
- Building integrations that need to act as multiple users
- Maintaining proper user attribution in audit logs
- Exercising all APIs without managing individual user tokens
Why Use This Approach?
- Security: Service accounts have controlled scopes and can be revoked
- Flexibility: One service account can impersonate any user in your business
- Audit Trail: All actions are logged with both the service account and impersonated user
- Efficiency: No need to create and manage tokens for each user
- Testing: Easily test APIs with different user roles and permissions
Complete API Call Flow
High-Level Flow Diagram
Step-by-Step Implementation
Step 1: Create a Service Account Token
Prerequisites:
- Admin access to MangoApps
- Understanding of required permission scopes
Process:
- Navigate to Admin Interface
- Go to Admin → API → Tokens
- Click Create Service Account Token
- Configure Token Settings
- Name: Descriptive name (e.g., “Integration Service Account”)
- Service Account: Check this box (required)
- Scopes: Select
impersonatescope (oradminfor full access) - Expiration: Set appropriate expiration date
- Business: Select the business this token applies to
- Save and Copy Credentials
- Click Create Token
- IMPORTANT: Copy both the Token and Secret immediately
- These values are shown only once and cannot be retrieved later
Example Response:
{
"token": "svc_abc123def456ghi789",
"secret": "sec_xyz789uvw456rst123",
"name": "Integration Service Account",
"scopes": ["impersonate"],
"expires_at": "2026-01-15T10:30:00Z"
}
Security Notes:
- Store credentials securely (environment variables, secret managers)
- Never commit credentials to version control
- Use different tokens for different environments (dev/staging/prod)
Step 2: Create an Impersonation Token
Endpoint: POST /api/v1/auth/impersonate
Authentication: Use your service account token
Request Headers:
Authorization: Bearer YOUR_SERVICE_ACCOUNT_TOKEN
X-API-Secret: YOUR_SERVICE_ACCOUNT_SECRET
Content-Type: application/json
Request Body:
{
"user_id": 123,
"expires_in": 3600
}
Parameters:
| Parameter | Type | Required | Description | Default |
|———–|——|———-|————-|———|
| user_id | integer | Yes | ID of user to impersonate | - |
| expires_in | integer | No | Token expiration in seconds (max 86400) | 3600 |
Example Request (cURL):
curl -X POST "https://your-domain.com/api/v1/auth/impersonate" \
-H "Authorization: Bearer svc_abc123def456ghi789" \
-H "X-API-Secret: sec_xyz789uvw456rst123" \
-H "Content-Type: application/json" \
-d '{
"user_id": 123,
"expires_in": 3600
}'
Example Response:
{
"token": "imp_xyz987abc654def321",
"expires_at": "2025-01-15T11:30:00Z",
"impersonated_user": {
"id": 123,
"email": "user@example.com",
"name": "John Doe"
},
"instructions": {
"usage": "Use this token as a regular Bearer token in the Authorization header",
"example": "Authorization: Bearer imp_xyz987abc654def321"
}
}
Error Responses:
403 Forbidden - Service Account Required:
{
"error": {
"code": "service_account_required",
"message": "Impersonation requires service account authentication"
}
}
403 Forbidden - Insufficient Scope:
{
"error": {
"code": "insufficient_scope",
"message": "Service account requires 'impersonate' or 'admin' scope"
}
}
404 Not Found - User Not Found:
{
"error": {
"code": "user_not_found",
"message": "User not found in current business"
}
}
Step 3: Use Impersonation Token for API Calls
Once you have an impersonation token, use it exactly like a regular user API token.
Request Headers:
Authorization: Bearer YOUR_IMPERSONATION_TOKEN
Content-Type: application/json
Important: Do NOT include X-API-Secret header when using impersonation tokens. Only service account tokens require the secret.
Example: Get User’s Shifts
curl -X GET "https://your-domain.com/api/v1/shifts" \
-H "Authorization: Bearer imp_xyz987abc654def321" \
-H "Content-Type: application/json"
Example: Create Leave Request
curl -X POST "https://your-domain.com/api/v1/leave_requests" \
-H "Authorization: Bearer imp_xyz987abc654def321" \
-H "Content-Type: application/json" \
-d '{
"start_date": "2025-02-01",
"end_date": "2025-02-05",
"leave_type_id": 1,
"reason": "Vacation"
}'
What Happens Behind the Scenes:
- API validates the impersonation token
- Extracts the impersonated user context
- Executes the API call as that user
- Logs the action with both service account and user information
- Returns response scoped to the impersonated user’s permissions
Complete Integration Example
Python Example
import requests
import os
from datetime import datetime, timedelta
class MangoAppsAPIClient:
def __init__(self, base_url, service_token, service_secret):
self.base_url = base_url
self.service_token = service_token
self.service_secret = service_secret
self.impersonation_tokens = {} # Cache tokens by user_id
def _get_service_headers(self):
"""Get headers for service account authentication"""
return {
'Authorization': f'Bearer {self.service_token}',
'X-API-Secret': self.service_secret,
'Content-Type': 'application/json'
}
def _get_impersonation_headers(self, impersonation_token):
"""Get headers for impersonation token authentication"""
return {
'Authorization': f'Bearer {impersonation_token}',
'Content-Type': 'application/json'
}
def create_impersonation_token(self, user_id, expires_in=3600):
"""Create an impersonation token for a specific user"""
url = f'{self.base_url}/api/v1/auth/impersonate'
payload = {
'user_id': user_id,
'expires_in': expires_in
}
response = requests.post(
url,
headers=self._get_service_headers(),
json=payload
)
response.raise_for_status()
data = response.json()
self.impersonation_tokens[user_id] = {
'token': data['token'],
'expires_at': datetime.fromisoformat(data['expires_at'].replace('Z', '+00:00')),
'user': data['impersonated_user']
}
return data['token']
def get_impersonation_token(self, user_id, refresh_if_expired=True):
"""Get cached impersonation token or create new one"""
if user_id in self.impersonation_tokens:
token_data = self.impersonation_tokens[user_id]
# Check if token is expired or will expire soon (within 5 minutes)
if token_data['expires_at'] > datetime.now(token_data['expires_at'].tzinfo) + timedelta(minutes=5):
return token_data['token']
elif refresh_if_expired:
# Token expired, create new one
return self.create_impersonation_token(user_id)
else:
# No cached token, create new one
return self.create_impersonation_token(user_id)
def get_user_shifts(self, user_id, start_date=None, end_date=None):
"""Get shifts for a specific user using impersonation"""
# Get or create impersonation token
imp_token = self.get_impersonation_token(user_id)
url = f'{self.base_url}/api/v1/shifts'
params = {}
if start_date:
params['start_date'] = start_date
if end_date:
params['end_date'] = end_date
response = requests.get(
url,
headers=self._get_impersonation_headers(imp_token),
params=params
)
response.raise_for_status()
return response.json()
def create_leave_request(self, user_id, start_date, end_date, leave_type_id, reason=None):
"""Create a leave request for a specific user using impersonation"""
imp_token = self.get_impersonation_token(user_id)
url = f'{self.base_url}/api/v1/leave_requests'
payload = {
'start_date': start_date,
'end_date': end_date,
'leave_type_id': leave_type_id
}
if reason:
payload['reason'] = reason
response = requests.post(
url,
headers=self._get_impersonation_headers(imp_token),
json=payload
)
response.raise_for_status()
return response.json()
# Usage Example
if __name__ == '__main__':
# Initialize client with service account credentials
client = MangoAppsAPIClient(
base_url='https://your-domain.com',
service_token=os.getenv('MANGOOPS_SERVICE_TOKEN'),
service_secret=os.getenv('MANGOOPS_SERVICE_SECRET')
)
# Get shifts for user ID 123
shifts = client.get_user_shifts(user_id=123, start_date='2025-02-01', end_date='2025-02-28')
print(f"Found {len(shifts.get('data', []))} shifts")
# Create leave request for user ID 123
leave_request = client.create_leave_request(
user_id=123,
start_date='2025-02-10',
end_date='2025-02-14',
leave_type_id=1,
reason='Vacation'
)
print(f"Created leave request: {leave_request['id']}")
JavaScript/Node.js Example
const axios = require('axios');
class MangoAppsAPIClient {
constructor(baseUrl, serviceToken, serviceSecret) {
this.baseUrl = baseUrl;
this.serviceToken = serviceToken;
this.serviceSecret = serviceSecret;
this.impersonationTokens = new Map(); // Cache tokens by user_id
}
getServiceHeaders() {
return {
'Authorization': `Bearer ${this.serviceToken}`,
'X-API-Secret': this.serviceSecret,
'Content-Type': 'application/json'
};
}
getImpersonationHeaders(impersonationToken) {
return {
'Authorization': `Bearer ${impersonationToken}`,
'Content-Type': 'application/json'
};
}
async createImpersonationToken(userId, expiresIn = 3600) {
const url = `${this.baseUrl}/api/v1/auth/impersonate`;
const payload = {
user_id: userId,
expires_in: expiresIn
};
const response = await axios.post(url, payload, {
headers: this.getServiceHeaders()
});
const tokenData = {
token: response.data.token,
expiresAt: new Date(response.data.expires_at),
user: response.data.impersonated_user
};
this.impersonationTokens.set(userId, tokenData);
return response.data.token;
}
async getImpersonationToken(userId, refreshIfExpired = true) {
const cached = this.impersonationTokens.get(userId);
if (cached) {
// Check if token expires within 5 minutes
const expiresIn = cached.expiresAt.getTime() - Date.now();
if (expiresIn > 5 * 60 * 1000) {
return cached.token;
} else if (refreshIfExpired) {
return await this.createImpersonationToken(userId);
}
}
return await this.createImpersonationToken(userId);
}
async getUserShifts(userId, startDate = null, endDate = null) {
const impToken = await this.getImpersonationToken(userId);
const url = `${this.baseUrl}/api/v1/shifts`;
const params = {};
if (startDate) params.start_date = startDate;
if (endDate) params.end_date = endDate;
const response = await axios.get(url, {
headers: this.getImpersonationHeaders(impToken),
params
});
return response.data;
}
async createLeaveRequest(userId, startDate, endDate, leaveTypeId, reason = null) {
const impToken = await this.getImpersonationToken(userId);
const url = `${this.baseUrl}/api/v1/leave_requests`;
const payload = {
start_date: startDate,
end_date: endDate,
leave_type_id: leaveTypeId
};
if (reason) payload.reason = reason;
const response = await axios.post(url, payload, {
headers: this.getImpersonationHeaders(impToken)
});
return response.data;
}
}
// Usage Example
async function main() {
const client = new MangoAppsAPIClient(
'https://your-domain.com',
process.env.MANGOOPS_SERVICE_TOKEN,
process.env.MANGOOPS_SERVICE_SECRET
);
// Get shifts for user ID 123
const shifts = await client.getUserShifts(123, '2025-02-01', '2025-02-28');
console.log(`Found ${shifts.data?.length || 0} shifts`);
// Create leave request for user ID 123
const leaveRequest = await client.createLeaveRequest(
123,
'2025-02-10',
'2025-02-14',
1,
'Vacation'
);
console.log(`Created leave request: ${leaveRequest.id}`);
}
main().catch(console.error);
Managing Impersonation Tokens
List Active Impersonation Tokens
Endpoint: GET /api/v1/auth/impersonate
Authentication: Service account token
Example Request:
curl -X GET "https://your-domain.com/api/v1/auth/impersonate" \
-H "Authorization: Bearer YOUR_SERVICE_ACCOUNT_TOKEN" \
-H "X-API-Secret: YOUR_SERVICE_ACCOUNT_SECRET"
Example Response:
{
"items": [
{
"token": "imp_xyz987abc...",
"impersonated_user": {
"id": 123,
"email": "user@example.com",
"name": "John Doe"
},
"created_at": "2025-01-15T10:30:00Z",
"expires_at": "2025-01-15T11:30:00Z",
"revoked": false
}
],
"total_count": 1
}
Revoke an Impersonation Token
Endpoint: DELETE /api/v1/auth/impersonate/:token
Authentication: Service account token
Example Request:
curl -X DELETE "https://your-domain.com/api/v1/auth/impersonate/imp_xyz987abc654def321" \
-H "Authorization: Bearer YOUR_SERVICE_ACCOUNT_TOKEN" \
-H "X-API-Secret: YOUR_SERVICE_ACCOUNT_SECRET"
Example Response:
{
"message": "Impersonation token revoked successfully"
}
Security Best Practices
Service Account Security
- Use Least Privilege: Only grant
impersonatescope if that’s all you need - Separate Environments: Use different service accounts for dev/staging/prod
- Regular Rotation: Rotate service account tokens periodically (every 6-12 months)
- Secure Storage: Store tokens in environment variables or secret managers
- Monitor Usage: Regularly review service account usage logs
Impersonation Token Security
- Short Expiration: Use shortest expiration time that meets your needs (default 1 hour)
- Token Caching: Cache tokens but refresh before expiration
- Revoke When Done: Revoke tokens when no longer needed
- User Validation: Always validate user_id belongs to your business
- Audit Logging: Monitor impersonation token creation and usage
API Call Security
- HTTPS Only: Always use HTTPS for API calls
- Error Handling: Don’t expose tokens in error messages
- Rate Limiting: Respect API rate limits
- Request Validation: Validate all input before making API calls
- Logging: Log API calls but never log tokens or secrets
Common Use Cases
Use Case 1: Bulk Operations Across Users
Scenario: Need to create leave requests for multiple users
user_ids = [123, 456, 789]
for user_id in user_ids:
leave_request = client.create_leave_request(
user_id=user_id,
start_date='2025-03-01',
end_date='2025-03-05',
leave_type_id=1
)
print(f"Created request for user {user_id}: {leave_request['id']}")
Use Case 2: Testing APIs with Different Roles
Scenario: Test API behavior with different user roles
# Test as regular employee
employee_token = client.create_impersonation_token(employee_user_id)
# Make API calls with employee_token
# Test as manager
manager_token = client.create_impersonation_token(manager_user_id)
# Make API calls with manager_token
# Test as admin
admin_token = client.create_impersonation_token(admin_user_id)
# Make API calls with admin_token
Use Case 3: Scheduled Background Jobs
Scenario: Run scheduled jobs that act as different users
def scheduled_job():
# Get list of users who need processing
users = get_users_needing_processing()
for user in users:
# Create impersonation token for this user
imp_token = client.get_impersonation_token(user['id'])
# Perform operations as this user
process_user_data(user['id'], imp_token)
Troubleshooting
Issue: “Service account required” Error
Problem: Getting 403 error when trying to create impersonation token
Solution:
- Verify you’re using a service account token (not a user token)
- Ensure both
AuthorizationandX-API-Secretheaders are included - Check that token hasn’t expired or been revoked
Issue: “Insufficient scope” Error
Problem: Getting 403 error about insufficient scope
Solution:
- Verify service account token has
impersonateoradminscope - Create new service account token with correct scopes
- Check token configuration in admin interface
Issue: “User not found” Error
Problem: Getting 404 error when creating impersonation token
Solution:
- Verify user_id exists in your business
- Check that user belongs to the same business as service account
- Ensure user_id is correct integer value
Issue: Impersonation Token Expired
Problem: Getting 401 error when using impersonation token
Solution:
- Check token expiration time
- Implement token refresh logic (create new token before expiration)
- Cache tokens with expiration tracking
Issue: API Calls Return Wrong User Context
Problem: API calls seem to be executed as wrong user
Solution:
- Verify you’re using impersonation token (not service account token)
- Ensure you’re not including
X-API-Secretheader with impersonation token - Check token hasn’t been revoked
API Reference Summary
Impersonation Endpoints
| Method | Endpoint | Description | Auth Required |
|---|---|---|---|
| POST | /api/v1/auth/impersonate |
Create impersonation token | Service Account |
| GET | /api/v1/auth/impersonate |
List active tokens | Service Account |
| DELETE | /api/v1/auth/impersonate/:token |
Revoke token | Service Account |
Required Scopes
| Scope | Description |
|---|---|
impersonate |
Allows creating impersonation tokens |
admin |
Full access including impersonation |
Token Expiration
- Default: 1 hour (3600 seconds)
- Maximum: 24 hours (86400 seconds)
- Minimum: 60 seconds
Additional Resources
Related Documentation
- API Authentication Guide: Complete authentication system overview
- API Configuration: Setting up API access
- Custom API: Building custom API endpoints
Developer Tools
- OpenAPI Specification:
/api/v1/docs- Interactive API documentation - Postman Collection: Available in API documentation
- SDK Libraries: Check for language-specific SDKs
Quick Reference
Complete Flow Checklist
- Create service account token with
impersonatescope - Store service account token and secret securely
- Create impersonation token for target user
- Use impersonation token for API calls (no secret header)
- Implement token refresh logic
- Handle errors gracefully
- Revoke tokens when done
- Monitor usage and audit logs
Code Snippet Template
# 1. Initialize client
client = MangoAppsAPIClient(base_url, service_token, service_secret)
# 2. Get impersonation token
imp_token = client.get_impersonation_token(user_id)
# 3. Make API call
response = requests.get(
f'{base_url}/api/v1/shifts',
headers={'Authorization': f'Bearer {imp_token}'}
)
This guide covers using system accounts and impersonation for API integration. For general API authentication, see the API Authentication Guide. Last updated: January 2025.