Quality Evaluation bulk endpoint throwing 400 on payload structure

Sorry for the noob question again. Pushing a batch of quality evaluations through the /api/v2/wf/quality/evaluations endpoint with the genesyscloud python sdk v2.1.4 keeps failing. Single eval calls work fine, but the bulk route returns a hard 400. Saw a community thread from late last year mentioning the evaluationDate field needs strict ISO 8601 formatting, so the timestamps got double checked. Still breaking.

Here is the payload structure getting sent:

eval_batch = {
 "items": [
 {
 "form_id": "a1b2c3d4-5678-90ef-ghij-klmnopqrstuv",
 "evaluationDate": "2024-05-10T14:30:00.000Z",
 "agentId": "agent-uuid-here",
 "score": 85,
 "comments": "needs improvement on hold time"
 }
 ]
}

The sdk throws a genesyscloud.rest.rest_client.ApiException: (400) Reason: Bad Request. Response body just returns {"message":"Invalid payload structure","errors":[]}. Looks like the divisionId might be missing, but the swagger docs say it’s optional when the correct scope is attached. The service account definitely has quality:evaluation:write and quality:evaluation:read mapped. You’ll probably want to verify the division scope if the tenant uses multi-division routing.

Authentication runs through client credentials flow and the token refreshes without issues. Pagination isn’t even relevant here since it’s a single batch request. Maybe the score field expects a float instead of an int? Tried casting it to 85.0 but the error stays exactly the same.

Any chance the bulk endpoint requires a specific content-type header that the sdk isn’t setting by default? Stepped through the sdk source code and the request headers look completely standard. We’ve got the standard application/json set. The debug logs are doing jack all right now.

{"timestamp": "2024-05-10T19:15:22.000Z", "path": "/api/v2/wf/quality/evaluations", "status": 400, "error": "invalid_payload"}

genesys-cloud-python-sdk wraps bulk quality calls in a BulkApiRequest object, not a raw list. If you’re just passing an array of evaluation dicts to post_quality_evaluations, the serializer drops the required items wrapper and the platform rejects it with a 400. Strip out evaluationDate, id, and dateCreated before pushing. The platform auto-generates those on bulk ingest anyway.

Try restructuring the payload like this:

from genesyscloud.rest.v2.quality_api import QualityApi
from genesyscloud.model.bulk_api_request import BulkApiRequest
from genesyscloud.model.quality_evaluation import QualityEvaluation

eval_items = [
 QualityEvaluation(
 scorecard_id="your-scorecard-id",
 conversation_id="conversation-uuid",
 evaluator_id="evaluator-uuid",
 status="completed",
 score=85
 )
]

bulk_payload = BulkApiRequest(items=eval_items)
api_instance.post_quality_evaluations(body=bulk_payload)

The SDK’s type hints are a bit loose on bulk endpoints. It won’t catch the missing wrapper until runtime. Don’t pack the full object graph if you don’t need to. Those extra fields will just choke the parser. Also check that scorecard_id uses underscores instead of hyphens in the model constructor. If the payload still bounces, dump the raw request body with api_instance.call_api(..., debug=True) to see what the serializer actually ships. Usually the evaluationDate field is the culprit even when it looks correct, because the bulk route hardcodes a server-side timestamp override.

Are you routing this through a Data Action or hitting the SDK directly?

Problem
Raw arrays just break the serializer. Weird how it handles that.

Code

req = BulkApiRequest(items=eval_batch)
platformClient.post_quality_evaluations(body=req)

Error
Missing the wrapper triggers a hard 400.

Question
You’re stripping evaluationDate first?