We’re trying to build a custom dashboard that shows CSAT scores directly on the conversation detail view, but the data models are fighting us.
I’m using the Node.js SDK to fetch evaluations via client.qualityApi.postQualityEvaluationsQuery. The response gives me interactionId, which is great, but when I pull the survey results from /api/v2/surveys/results, the link back to that specific interaction is buried or missing entirely depending on the channel.
Here’s the JS snippet I’m using to try and map them:
const evals = await client.qualityApi.postQualityEvaluationsQuery({
body: {
query: { filter: { field: 'surveyScore', operator: 'notNull' } }
}
});
// Trying to find the matching survey result
const surveyId = evals.entities[0].surveyResultId;
const surveyDetails = await client.surveysApi.getSurveyResult(surveyId);
The problem is surveyResultId is often null for web chat interactions, even though the customer definitely submitted a score. The interactionId from the evaluation doesn’t match the conversationId in the survey result payload either.
Is there a different endpoint or a specific field I’m missing to bridge this gap? We can’t use the analytics summary reports because we need the raw response text attached to the specific agent’s interaction history.
Any hints on how to reliably join these two datasets in code?
The issue is usually that you’re looking for a direct ID match in the survey payload, but CXone stores the link differently. The interactionId from the Quality API is the internal conversation ID. The Survey API result doesn’t always expose that ID directly in the top-level response for every survey type.
You need to look at the surveyResponse object inside the result. Specifically, check the interaction field or the metadata section depending on your survey version. If you are using the standard CXone Surveys, the link is often found via the contactId or conversationId fields in the survey response metadata, not the interactionId used in Quality.
Here is a Node.js using the PureCloud SDK to bridge the gap. You fetch the evaluation, get the interactionId, then query the survey API with that ID as a filter if your survey type supports it, or more commonly, you have to match on the contactId if available in both datasets.
const purecloudApi = require('@genesyscloud/purecloud-sdk-nodejs');
const platformClient = purecloudApi.PlatformClient;
async function linkQualityToSurvey() {
const client = platformClient.initialize({
basePath: 'https://api.us.genesyscloud.com',
clientId: process.env.CXONE_CLIENT_ID,
clientSecret: process.env.CXONE_CLIENT_SECRET,
});
await client.login();
// 1. Get the evaluation with the interactionId
const qualityApi = new purecloudApi.QualityApi();
const qualityResponse = await qualityApi.postQualityEvaluationsQuery({
body: {
query: {
filters: [
{
field: 'id',
operator: 'eq',
value: 'your-evaluation-id-here' // Replace with actual ID
}
]
}
}
});
const evaluation = qualityResponse.results[0];
const interactionId = evaluation.interaction.id;
const contactId = evaluation.interaction.contactId; // This is the key bridge often
// 2. Query surveys. Note: Survey API doesn't always allow filtering by interactionId directly in all versions.
// You often have to fetch recent surveys and match on contactId or timestamp.
const surveysApi = new purecloudApi.SurveysApi();
const surveyResponse = await surveysApi.getSurveysResults({
pageSize: 25,
sortBy: 'submittedAt',
sortOrder: 'desc'
});
// 3. Find the matching survey by comparing contactId or metadata
const matchedSurvey = surveyResponse.results.find(survey => {
// Check if the survey response contains the contact ID from the interaction
// The structure can vary slightly based on survey type (CSAT vs custom)
return survey.response.contactId === contactId ||
(survey.metadata && survey.metadata.conversationId === interactionId);
});
if (matchedSurvey) {
console.log('Linked Survey:', matchedSurvey);
} else {
console.log('No matching survey found for interaction:', interactionId);
}
}
linkQualityToSurvey();
If the contactId isn’t present in the survey response, you might need to use the REST Proxy in to capture the interactionId at the end of the flow and push it to your backend alongside the survey submission. That way you control the join key. The native API link is sometimes weak for older survey types.