We’ve been pulling GET /api/v2/quality/evaluations with a query payload to extract CSAT scores linked to specific conversations. The filter block uses field: 'formId' and op: 'eq'. Response JSON gives interactionId as null on roughly 18% of the rows. Dashboard aggregation works fine when we ignore those gaps. The pagingToken from page 3 throws a 400 Bad Request if we pass it back with the original query body. Our Python script uses requests.post with standard Authorization headers. The dateFrom and dateTo values are UTC formatted to match Manila shift boundaries. Metric calculations in the reporting layer break when the conversationId join fails. Switching to GET /api/v2/analytics/conversations/details/queries doesn’t help because the csat_score metric lacks the raw survey text. The surveyResponse field inside the evaluation payload contains a nested answers array. We’re mapping answers[0].value to the conversation lookup table. Some records return conversationId but others only have wrapUpCode. The SDK method quality_api.get_quality_evaluations handles paging automatically but drops the query filter on the second call. We need a way to force the join. The 400 error body says invalid paging token for filtered query. Clock skew isn’t the problem here. We’ve added Accept: application/json to the headers. The formId matches our CSAT survey exactly. The join keeps dropping records after the second page. We’ve been parsing the answers array manually but the interactionId gap stays.
CSAT records don’t actually store an interactionId because the survey isn’t tied to a specific conversation leg until you link it manually. You’ll need to join on surveyId instead.