Delete a User via Genesys Cloud API Without Breaking Historical Data
What You Will Build
- This script deletes a specific user from Genesys Cloud while ensuring that all historical conversation records, analytics data, and call logs remain intact and queryable.
- This tutorial uses the Genesys Cloud Platform Client V2 SDK and the REST API endpoints for user management and identity verification.
- The implementation is provided in Python 3.10+ using the
genesyscloudSDK andhttpxfor raw API fallbacks.
Prerequisites
- OAuth Client Type: Machine-to-Machine (M2M) application.
- Required Scopes:
user:read(to verify user status and identity)user:delete(to perform the deletion)identity:read(optional, for debugging identity mappings)
- SDK Version:
genesyscloudPython SDK version 150.0.0 or higher. - Runtime Requirements: Python 3.10+.
- External Dependencies:
genesyscloudhttpx(for direct API calls if needed)python-dotenv(for secure credential management)
Install dependencies:
pip install genesyscloud httpx python-dotenv
Authentication Setup
Genesys Cloud uses OAuth 2.0 Client Credentials flow for M2M applications. The SDK handles token acquisition and refresh automatically when initialized correctly. You must configure the environment variables for your client ID and secret.
Create a .env file:
GENESYS_CLOUD_REGION=us-east-1
GENESYS_CLOUD_CLIENT_ID=your_client_id
GENESYS_CLOUD_CLIENT_SECRET=your_client_secret
Initialize the SDK client:
import os
from dotenv import load_dotenv
from genesyscloud.platform.client import PlatformClient
from genesyscloud.configuration import Configuration
load_dotenv()
def init_client() -> PlatformClient:
"""
Initializes the Genesys Cloud Platform Client with M2M OAuth.
"""
config = Configuration(
region=os.getenv("GENESYS_CLOUD_REGION"),
client_id=os.getenv("GENESYS_CLOUD_CLIENT_ID"),
client_secret=os.getenv("GENESYS_CLOUD_CLIENT_SECRET")
)
# The SDK automatically fetches the access token upon first API call
client = PlatformClient(config)
return client
Implementation
Step 1: Validate User Existence and Status
Before deleting a user, you must confirm the user exists and is not currently active in a way that would cause immediate data loss or session conflicts. While Genesys Cloud handles most historical data preservation automatically, it is best practice to verify the user ID and ensure the user is not currently engaged in a live interaction that might be interrupted.
Endpoint: GET /api/v2/users/{id}
Scope: user:read
from genesyscloud.api.users import UsersApi
from genesyscloud.rest import ApiException
def validate_user(client: PlatformClient, user_id: str) -> dict:
"""
Retrieves user details to confirm existence and current status.
Raises an error if the user is not found.
"""
api_instance = UsersApi(client)
try:
# Fetch the user object
user_response = api_instance.get_user_by_id(user_id=user_id)
if user_response is None:
raise ValueError(f"User with ID {user_id} not found.")
print(f"Found user: {user_response.name} (ID: {user_response.id})")
print(f"Current status: {user_response.status}")
return user_response
except ApiException as e:
if e.status == 404:
raise ValueError(f"User with ID {user_id} does not exist.")
elif e.status == 401:
raise PermissionError("Authentication failed. Check Client ID/Secret.")
else:
raise e
Step 2: Understanding Historical Data Preservation
It is critical to understand that deleting a user in Genesys Cloud does not delete their historical interaction data.
When a user is deleted:
- The user object is removed from the active directory.
- All historical conversations (calls, chats, emails, tasks) associated with that user ID remain in the data lake.
- Analytics reports (
/api/v2/analytics/conversations/...) will still return records containing the deleted user’s ID. - The user’s name and email are preserved in the historical record context. If you query a conversation detail later, the participant object will still contain the
nameandemailof the user at the time of the interaction, or the cached identity profile.
However, if you need to ensure that the user cannot be reactivated or associated with new data, you might consider archiving their data first. For most compliance scenarios, simple deletion is sufficient because the data is immutable in the analytics store.
If you are concerned about data retention policies, you should verify the user’s last interaction date. This step is optional but recommended for audit trails.
Endpoint: GET /api/v2/analytics/conversations/details/query
Scope: analytics:read
from genesyscloud.api.analytics import AnalyticsApi
from datetime import datetime, timedelta
def check_last_interaction(client: PlatformClient, user_id: str, days_back: int = 90) -> bool:
"""
Checks if the user has had interactions in the last N days.
Returns True if interactions exist, False otherwise.
This is for informational purposes only; deletion will proceed regardless.
"""
api_instance = AnalyticsApi(client)
# Define the time window
end_time = datetime.utcnow().isoformat() + "Z"
start_time = (datetime.utcnow() - timedelta(days=days_back)).isoformat() + "Z"
# Build the query body
query_body = {
"dateFrom": start_time,
"dateTo": end_time,
"pageSize": 1,
"view": "default",
"select": ["id"],
"filter": [
{
"type": "fieldValue",
"field": "wrapupcode",
"value": ["*"] # Placeholder to ensure query validity; usually filter by participant
},
{
"type": "fieldValue",
"field": "participantIds",
"value": [user_id]
}
]
}
try:
response = api_instance.post_analytics_conversations_details_query(body=query_body)
if response and response.entities and len(response.entities) > 0:
print(f"Warning: User {user_id} has recent interactions in the last {days_back} days.")
return True
else:
print(f"No recent interactions found for user {user_id}.")
return False
except ApiException as e:
# If the query fails or returns 404 (no data), assume no interactions
print(f"Could not verify interaction history: {e.reason}")
return False
Step 3: Delete the User
The actual deletion is performed via the DELETE /api/v2/users/{id} endpoint. This operation is irreversible for the user object itself, but as noted, historical data remains.
Endpoint: DELETE /api/v2/users/{id}
Scope: user:delete
def delete_user(client: PlatformClient, user_id: str) -> bool:
"""
Deletes the specified user from Genesys Cloud.
Returns True if successful, raises exception if failed.
"""
api_instance = UsersApi(client)
try:
# Perform the deletion
api_instance.delete_user_by_id(user_id=user_id)
print(f"Successfully deleted user with ID: {user_id}")
return True
except ApiException as e:
if e.status == 404:
raise ValueError(f"User with ID {user_id} not found during deletion.")
elif e.status == 403:
raise PermissionError("Insufficient permissions to delete user. Check 'user:delete' scope.")
elif e.status == 409:
# Conflict might occur if the user is currently in a call or has active sessions
raise RuntimeError(f"Conflict: User {user_id} is currently active or locked. Wait for sessions to end.")
else:
raise e
Complete Working Example
This script combines all steps into a single executable module. It validates the user, checks for recent activity (optional warning), and then deletes the user.
import os
import sys
import argparse
from dotenv import load_dotenv
from genesyscloud.platform.client import PlatformClient
from genesyscloud.configuration import Configuration
from genesyscloud.api.users import UsersApi
from genesyscloud.api.analytics import AnalyticsApi
from genesyscloud.rest import ApiException
from datetime import datetime, timedelta
# Load environment variables
load_dotenv()
def init_client() -> PlatformClient:
"""
Initializes the Genesys Cloud Platform Client with M2M OAuth.
"""
config = Configuration(
region=os.getenv("GENESYS_CLOUD_REGION"),
client_id=os.getenv("GENESYS_CLOUD_CLIENT_ID"),
client_secret=os.getenv("GENESYS_CLOUD_CLIENT_SECRET")
)
if not config.region or not config.client_id or not config.client_secret:
raise ValueError("Missing required environment variables: GENESYS_CLOUD_REGION, GENESYS_CLOUD_CLIENT_ID, GENESYS_CLOUD_CLIENT_SECRET")
client = PlatformClient(config)
return client
def validate_user(client: PlatformClient, user_id: str) -> dict:
"""
Retrieves user details to confirm existence and current status.
"""
api_instance = UsersApi(client)
try:
user_response = api_instance.get_user_by_id(user_id=user_id)
if user_response is None:
raise ValueError(f"User with ID {user_id} not found.")
print(f"Found user: {user_response.name} (ID: {user_response.id})")
print(f"Current status: {user_response.status}")
print(f"Email: {user_response.email}")
return user_response
except ApiException as e:
if e.status == 404:
raise ValueError(f"User with ID {user_id} does not exist.")
elif e.status == 401:
raise PermissionError("Authentication failed. Check Client ID/Secret.")
else:
raise e
def check_last_interaction(client: PlatformClient, user_id: str, days_back: int = 90) -> bool:
"""
Checks if the user has had interactions in the last N days.
"""
api_instance = AnalyticsApi(client)
end_time = datetime.utcnow().isoformat() + "Z"
start_time = (datetime.utcnow() - timedelta(days=days_back)).isoformat() + "Z"
query_body = {
"dateFrom": start_time,
"dateTo": end_time,
"pageSize": 1,
"view": "default",
"select": ["id"],
"filter": [
{
"type": "fieldValue",
"field": "participantIds",
"value": [user_id]
}
]
}
try:
response = api_instance.post_analytics_conversations_details_query(body=query_body)
if response and response.entities and len(response.entities) > 0:
print(f"Warning: User {user_id} has recent interactions in the last {days_back} days.")
return True
else:
print(f"No recent interactions found for user {user_id}.")
return False
except ApiException as e:
print(f"Could not verify interaction history (non-fatal): {e.reason}")
return False
def delete_user(client: PlatformClient, user_id: str) -> bool:
"""
Deletes the specified user from Genesys Cloud.
"""
api_instance = UsersApi(client)
try:
api_instance.delete_user_by_id(user_id=user_id)
print(f"Successfully deleted user with ID: {user_id}")
return True
except ApiException as e:
if e.status == 404:
raise ValueError(f"User with ID {user_id} not found during deletion.")
elif e.status == 403:
raise PermissionError("Insufficient permissions to delete user. Check 'user:delete' scope.")
elif e.status == 409:
raise RuntimeError(f"Conflict: User {user_id} is currently active or locked. Wait for sessions to end.")
else:
raise e
def main():
parser = argparse.ArgumentParser(description="Delete a Genesys Cloud user while preserving historical data.")
parser.add_argument("--user-id", required=True, help="The ID of the user to delete.")
parser.add_argument("--check-history", action="store_true", help="Check for recent interactions before deleting.")
parser.add_argument("--days-back", type=int, default=90, help="Number of days to check for history.")
args = parser.parse_args()
try:
# 1. Initialize Client
print("Initializing Genesys Cloud Client...")
client = init_client()
# 2. Validate User
print("Validating user...")
validate_user(client, args.user_id)
# 3. Optional History Check
if args.check_history:
check_last_interaction(client, args.user_id, args.days_back)
# 4. Delete User
print("Proceeding with deletion...")
delete_user(client, args.user_id)
print("Operation completed successfully.")
except Exception as e:
print(f"Error: {e}", file=sys.stderr)
sys.exit(1)
if __name__ == "__main__":
main()
Run the script:
python delete_user.py --user-id 12345678-1234-1234-1234-123456789abc --check-history
Common Errors & Debugging
Error: 403 Forbidden
- Cause: The OAuth client lacks the
user:deletescope. - Fix: Go to the Genesys Cloud Admin Console, navigate to Administration > Security > Applications, edit your M2M app, and ensure
user:deleteis checked under the Scopes tab.
Error: 409 Conflict
- Cause: The user is currently logged in, in a call, or has an active session.
- Fix: Wait for the user to log out or for their session to timeout. You can also force logout via the API if necessary, but deletion is safer when the user is inactive.
Error: 404 Not Found
- Cause: The user ID provided is invalid or the user has already been deleted.
- Fix: Verify the user ID in the Genesys Cloud Admin Console. Ensure you are using the correct ID format (UUID).
Error: Historical Data Disappears
- Cause: This should not happen with standard deletion. If historical data is missing, it may be due to a separate data retention policy or a bulk data purge operation.
- Fix: Check Administration > Data Privacy > Data Retention settings. Ensure no retention policies are set to delete conversation data immediately upon user deletion (which is not a standard default setting).