Python service for CXone Agent Assist quality scoring hitting 400 on QM API submission

Building a Python service to automate quality scoring for NICE CXone Agent Assist. Fetching interaction transcripts via GET /api/v2/quality/evaluations/{evaluationId}/interaction, running them through a custom parser for compliance checks, calculating composite scores, then submitting ratings to the Quality Management API with detailed comment breakdowns. The scoring logic works fine locally. Parser spits out a JSON object with the rubric breakdown.

Tried submitting via POST /api/v2/quality/evaluations. Got a 400 Bad Request immediately. Response body just says validation failed on the scoringCriteria array. Checked the docs three times. Structure matches the example payload.

Here is the request body being sent:
{
“evaluationFormId”: “a1b2c3d4-e5f6-7890-abcd-ef1234567890”,
“interactionId”: “int-98765432”,
“interactionType”: “voice”,
“scoringCriteria”: [
{
“scoringCriteriaId”: “criterion-001”,
“score”: 85.5,
“comment”: “Missed opening protocol but handled escalation properly”
},
{
“scoringCriteriaId”: “criterion-002”,
“score”: 92.0,
“comment”: “Compliance statements delivered accurately”
}
],
“totalScore”: 88.75
}

Swapped the token grant type from client_credentials to password_grant just in case the service account lacked permissions. Still hitting 400. Increased the timeout to 30 seconds. No change. The parser outputs floats for scores, so I converted them to strings before serializing. Same error.

Docs mention a maxCommentLength property on the evaluation form itself. Didn’t account for that in the initial run. Truncated the comment strings to 200 characters. Request still fails with the exact same 400 payload.

Python 3.10 using requests library. Authentication header is formatted as Bearer . Content-Type set to application/json. Looking at the raw response headers, there’s no correlation ID returned. Makes debugging this a pain. The validator is definitely choking on the array structure. IDs pull directly from the form definition endpoint. Scores fall within the 0-100 range. Comments are plain text. Interaction type matches the transcript source.

Tried flattening the array to a single object. API expects an array. Went back to the list format. Added a dummy ratingStatus field. Still 400. Clock’s running late here in Seoul so I might be missing something obvious. We’ve been staring at this endpoint for hours. The payload structure looks identical to the working manual submissions in the portal. Don’t see where the mismatch is. It’s probably a schema version mismatch on the endpoint. Leaving it here.