API Integration with System Accounts and Impersonation

System Features Special Features Api
Last updated: January 26, 2026 • Version: 1.0

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

sequenceDiagram participant Dev as Developer/App participant API as MangoApps API participant SA as Service Account participant IT as Impersonation Token participant User as Target User Dev->>API: 1. Create Service Account Token Note over Dev,API: Admin creates token with 'impersonate' scope API-->>Dev: Service Account Token + Secret Dev->>API: 2. Request Impersonation Token Note over Dev,API: POST /api/v1/auth/impersonate<br/>Authorization: Bearer SA_TOKEN<br/>X-API-Secret: SA_SECRET<br/>Body: {user_id: 123} API->>SA: Validate Service Account SA->>API: Valid with 'impersonate' scope API->>IT: Create Impersonation Token API-->>Dev: Impersonation Token + User Info Dev->>API: 3. Make API Calls as User Note over Dev,API: Authorization: Bearer IMPERSONATION_TOKEN<br/>All subsequent API calls API->>IT: Validate Impersonation Token IT->>User: Get User Context API->>API: Execute API Call as User API-->>Dev: Response with User Context

Step-by-Step Implementation

Step 1: Create a Service Account Token

Prerequisites:

  • Admin access to MangoApps
  • Understanding of required permission scopes

Process:

  1. Navigate to Admin Interface
    • Go to Admin → API → Tokens
    • Click Create Service Account Token
  2. Configure Token Settings
    • Name: Descriptive name (e.g., “Integration Service Account”)
    • Service Account: Check this box (required)
    • Scopes: Select impersonate scope (or admin for full access)
    • Expiration: Set appropriate expiration date
    • Business: Select the business this token applies to
  3. 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:

  1. API validates the impersonation token
  2. Extracts the impersonated user context
  3. Executes the API call as that user
  4. Logs the action with both service account and user information
  5. 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

  1. Use Least Privilege: Only grant impersonate scope if that’s all you need
  2. Separate Environments: Use different service accounts for dev/staging/prod
  3. Regular Rotation: Rotate service account tokens periodically (every 6-12 months)
  4. Secure Storage: Store tokens in environment variables or secret managers
  5. Monitor Usage: Regularly review service account usage logs

Impersonation Token Security

  1. Short Expiration: Use shortest expiration time that meets your needs (default 1 hour)
  2. Token Caching: Cache tokens but refresh before expiration
  3. Revoke When Done: Revoke tokens when no longer needed
  4. User Validation: Always validate user_id belongs to your business
  5. Audit Logging: Monitor impersonation token creation and usage

API Call Security

  1. HTTPS Only: Always use HTTPS for API calls
  2. Error Handling: Don’t expose tokens in error messages
  3. Rate Limiting: Respect API rate limits
  4. Request Validation: Validate all input before making API calls
  5. 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 Authorization and X-API-Secret headers 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 impersonate or admin scope
  • 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-Secret header 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

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 impersonate scope
  • 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.