I’m building an automated cleanup script using the Python SDK to remove offboarded agents from Genesys Cloud. The goal is to keep the historical interaction data intact for compliance and my OpenTelemetry tracing pipeline, which relies on external.user.id to correlate spans. When I call users.delete_user(user_id) directly, the API returns a 204 No Content, but the user object seems to vanish completely from the users endpoint. This breaks my trace aggregation logic because the span attributes reference the now-gone user ID. I tried checking if there’s a soft-delete flag or a specific header to preserve the record, but the docs are silent. I need the user to be inaccessible for login but still queryable by ID for historical context in my Jaeger backend. Here’s the snippet I’m using:
from genesyscloud import Configuration, ApiClient, UsersApi
cfg = Configuration()
cfg.host = "https://api.mypurecloud.com"
token = "<access_token>"
api_client = ApiClient(configuration=cfg)
api_client.default_headers["Authorization"] = f"Bearer {token}"
users_api = UsersApi(api_client)
try:
users_api.delete_user(user_id="abc-123-def")
print("User deleted")
except Exception as e:
print(f"Error: {e}")
Is there a way to disable the user instead of deleting them, or is there a different endpoint I should hit? The update_user method allows setting status to inactive, but that feels like a workaround. I’ve seen mentions of provisioning APIs, but those seem geared towards initial creation. I need a definitive way to remove access while keeping the identity record alive for my tracing correlation. The current behavior wipes the ID from the system, making it impossible to look up the user name in the trace details later. I don’t want to store a separate mapping table just for this. What’s the standard practice here?
You’re hitting a hard limit with the standard delete endpoint. It’s designed for a clean break, not a soft archive. If you want to keep the external.user.id for your OpenTelemetry spans, you can’t actually delete the user object. You’ll need to deactivate them instead.
The trick is to update the user’s routing settings and remove their role assignments, but leave the entity itself alone. This keeps the UUID stable in the database while stopping any new interactions from routing to them. Here’s how you handle it with the Python SDK:
from genesyscloud import PlatformClient
from genesyscloud.users.client import UsersClient
from genesyscloud.users.models import UserPresenceStatus
# Initialize client
platform_client = PlatformClient()
users_client = UsersClient(platform_client)
user_id = "your-user-id-here"
# 1. Get current user data
user, resp = users_client.get_user(user_id, expand=['routing'])
# 2. Set routing to offline
if user.routing:
user.routing.status = UserPresenceStatus(status='offline')
user.routing.availability_date_time = None # Clear availability
# 3. Update the user
users_client.put_user(
user_id,
body=user,
expand=['routing']
)
# 4. Optional: Remove specific roles if needed via roles_client
Warning: This doesn’t remove the user from queues or skill groups automatically. You’ll still need to iterate through their queue memberships and remove them if you want to ensure no new calls land there.
This approach preserves the user ID for your historical data and tracing pipeline. Your spans will still resolve correctly because the underlying entity hasn’t been destroyed. It’s a bit more work than a simple delete, but it saves you from losing that audit trail. Just make sure your cleanup script handles the role removal part carefully, as that can be a separate API call depending on your permission set.
Deactivating is the right call, but you need to watch out for the division assignment. If you just strip the roles and set routing.status to offline, the user stays active in the division. That means they can still be targeted by routing rules if you miss a config spot, or worse, they show up in user searches for internal tools.
Here’s the safer patch payload using PureCloudPlatformClientV2. You want to set routing.status to offline, clear the routing.skills, and crucially, set routing.availabilityStatus to offline if they’re currently online. This ensures no new interactions stick to that external.user.id.
from purecloud_platform_client import PlatformClient, UserRoutingSettings
pc = PlatformClient.init_from_config_file("config.yaml")
users_api = pc.users_api
user_id = "your-user-uuid"
user = users_api.get_user(user_id)
# Create routing settings update
routing_settings = UserRoutingSettings(
status="offline",
skills=[], # Clear skills to prevent accidental routing
availability_status="offline" # Force offline state
)
# Patch the user
users_api.patch_user(
user_id,
body={
"routing": routing_settings,
"roles": [] # Strip roles to prevent API access, but keep entity
}
)
Warning: Don’t touch the external.user.id field. If you clear it, your OpenTelemetry spans lose the correlation key entirely. The history stays, but the trace context breaks. Also, check your WEM adherence rules. Some configurations flag users with no roles as “inadherent” if they’re not properly deactivated in the WEM system, which can skew your compliance reports.
You’ll also want to verify that your custom desktop apps handle the offline status gracefully. If your SDK listener is waiting for a status change event, it might not fire if the user is already offline. You might need to poll the user status once after the patch to ensure the state propagates to all connected clients.
This approach keeps the UUID alive for your historical queries and trace aggregation. It’s not a true delete, but it’s the only way to keep the data intact without risking accidental re-routing.