Why does this setting of user attributes via Python SDK bulk creation fail with 400 Bad Request?

I am attempting to bulk-create users in Genesys Cloud using the Python Platform SDK by parsing a local CSV file. The goal is to automate onboarding for a new team in Lagos. I have successfully retrieved an OAuth token and instantiated the UsersApi client. However, when I iterate through the CSV rows and call create_user within a loop, I consistently receive a 400 Bad Request error for specific records. The error response indicates that the email field is already in use, which is expected for duplicates, but the issue is that the request fails entirely for valid, unique users immediately following a duplicate attempt, without allowing the loop to continue.

Here is the core logic of my script. I read the CSV using the csv module and construct a CreateUserRequest object for each row. I am explicitly setting the name, email, phone_numbers, and routing profiles. I am not using the batch endpoint because the documentation suggests create_user is idempotent or at least should not block subsequent calls if handled correctly. However, the SDK seems to throw an exception that breaks the loop entirely rather than returning a specific error for that user. I need to know how to handle partial failures gracefully. Should I be catching the ApiException specifically for 409 Conflict or 400 Bad Request and logging the error before continuing to the next row? Or is there a better pattern using the batch create endpoint /api/v2/users/users that I should be implementing instead to ensure atomicity or better error isolation?

Below is the snippet where the failure occurs. I am unsure if I am constructing the CreateUserRequest object correctly regarding the nested routing object. Any guidance on the correct SDK method call for robust bulk imports would be appreciated.

Ah, yeah, this is a known issue with bulk user creation where the SDK expects a specific nested structure for attributes, not a flat dictionary. The documentation for UsersApi states that attributes must be a UserAttributes object, but the Python client often fails to serialize this correctly in loops. Try constructing the request body manually using json.dumps before passing it to the API client to bypass the SDK’s serialization bug.

import json
from platformclientv2.models import CreateUserRequest, UserAttributes

# Construct the payload explicitly
attrs = UserAttributes()
attrs["custom_field"] = "value"
body = CreateUserRequest(
 name="Test User",
 email="[email protected]",
 attributes=attrs
)
# Verify serialization
print(json.dumps(body.to_dict(), default=str))

Take a look at at the attributes field in your CreateUserRequestBody, as it needs to be a map of strings, not a complex object. The 400 error is likely due to passing non-string values or nested JSON where the API expects simple key-value pairs like {"dept": "sales"}. Check your CSV parsing logic to ensure you aren’t accidentally serializing the attribute values into JSON strings before sending them to the SDK.

The official documentation states the Python SDK requires a UserAttributes instance, not a raw dict, which causes the serializer to drop the payload entirely.

from genesyscloud.models import UserAttributes, CreateUserRequestBody
attrs = UserAttributes(attributes={"role": "agent"})
body = CreateUserRequestBody(name="Test User", email="[email protected]", attributes=attrs)

Passing a dictionary directly results in a null payload on the wire, triggering the 400. Always instantiate the model objects.

TL;DR: Python SDK serialization is finicky.

It depends, but generally… the suggestion above about UserAttributes is correct. The SDK drops non-string values. Ensure your CSV parses to simple strings. I use a Lambda layer for this to avoid local env issues. Check the payload before sending.

# Verify attributes are strings
assert all(isinstance(v, str) for v in attrs.values())