Just noticed that the standard conversation termination flow in my Node.js gateway does not allow me to set a wrap-up code programmatically when I use DELETE /api/v2/conversations/webmessaging/{conversationId}. The endpoint simply closes the session without accepting a payload for disposition. I need to inject a specific code (e.g., “QA_Review”) into the interaction record before the wrap-up timer starts or immediately upon closure.
My GraphQL schema exposes a terminateConversation(id: ID!, wrapUpCode: String) mutation. I want this to be atomic. Currently, I am fetching the conversation, deleting it, and then attempting to update the interaction, but the timing is racy. The interaction state often transitions to “closed” before my subsequent PATCH request lands, resulting in a 409 Conflict or the update being silently ignored by the analytics engine.
Here is my current resolver logic using the Platform SDK:
const { ConversationApi } = require('@genesys/cloud/convapi');
const convApi = new ConversationApi();
async function terminateWithWrapUp(convId, code) {
// 1. End the conversation
await convApi.deleteConversationWebmessaging(convId);
// 2. Attempt to set wrap-up
// This fails because the conversation is already gone
const interactionId = await getInteractionIdFromCache(convId);
await convApi.putInteractionDisposition(interactionId, {
code: code
});
}
I have reviewed the API docs for PATCH /api/v2/conversations/webmessaging/{conversationId}. It accepts a state of closed but does not seem to support a wrapUpCode field in the body for the termination request itself.
- I tried chaining a
PATCH to the interaction endpoint immediately after the DELETE. The interaction ID is retrieved from a local Redis cache. The race condition causes the wrap-up to fail silently or return a 409.
- I attempted to use the
POST /api/v2/conversations/webmessaging/{conversationId}/wrapup endpoint, but this requires an active agent session context which my backend service does not have. It returns a 401 Unauthorized because I am using a machine-to-machine OAuth token without an associated user context.
Is there a supported API pattern to set the disposition code at the exact moment of programmatic termination for a machine-initiated conversation? Or should I be using a different endpoint entirely to bridge this gap in the GraphQL gateway?
3 Likes
the delete endpoint is strictly for tearing down the connection, so it ignores any disposition payload. you need to patch the participant resource first to set the wrap-up code before killing the session.
await fetch(`/api/v2/conversations/webmessaging/${id}/participants/${participantId}`, {
method: 'PATCH',
body: JSON.stringify({ wrapUpCode: 'QA_Review' })
});
await fetch(`/api/v2/conversations/webmessaging/${id}`, { method: 'DELETE' });
are you trying to set the code before the agent logs off or just tagging the record after? the DELETE endpoint won’t take a payload, that’s expected. you need to use the update method instead.
const { PureCloudPlatformClientV2 } = require('@genesyscloud/platform-client');
const platformClient = PureCloudPlatformClientV2.init();
async function setWrapUp(conversationId, codeName) {
try {
// 1. Fetch the code ID first
const codes = await platformClient.WfmQueueApi.getWfmQueueQueueswrapupcodes({
queueId: 'your-queue-id',
pageSize: 200
});
const codeId = codes.entities.find(c => c.name === codeName)?.id;
if (!codeId) throw new Error('Code not found');
// 2. Update conversation disposition
await platformClient.ConversationsApi.postConversationsConversationWrapup({
conversationId,
body: {
codeId: codeId,
notes: "Auto-set via gateway"
}
});
console.log('Wrap-up set successfully');
} catch (err) {
console.error('Failed to set wrap-up:', err);
}
}
make sure your OAuth token has conversation:write scope. this hits /api/v2/conversations/{conversationId}/wrapup. don’t try to patch the conversation object directly, it ignores the disposition field.
1 Like
patching the participant is the right move, but you’ll run into race conditions if the delete hits before the patch processes. the api is async, so you need a small delay or a polling check. also, make sure you’re using the correct participantId. if you’re using the gateway, that’s usually the bot or the agent, not the guest.
try this pattern. it waits for the patch to confirm before tearing down.
async function endWithWrapUp(convId, partId, code) {
// 1. set wrap-up
const patchRes = await fetch(`/api/v2/conversations/webmessaging/${convId}/participants/${partId}`, {
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ wrapUpCode: code })
});
if (!patchRes.ok) {
console.error('failed to set wrap-up:', await patchRes.text());
return;
}
// 2. small buffer to ensure backend processes it
await new Promise(r => setTimeout(r, 200));
// 3. close session
await fetch(`/api/v2/conversations/webmessaging/${convId}`, { method: 'DELETE' });
}
if you’re doing this for QA_Review, you might also want to tag the conversation. wrap-up codes are for reporting, tags are for filtering. setting both gives you better analytics later. the admin UI makes it easy to see wrap-up distribution, but tags help you pull specific interactions for review without digging through all ‘QA_Review’ codes if you have multiple reasons.
also, check your queue settings. if the wrap-up code is mandatory, the agent might get stuck in wrap-up state if the system doesn’t register the code correctly. i’ve seen that happen when the participantId was wrong. double check the participant object returned by the GET endpoint. it’s easy to mix up the guest and agent IDs.
one more thing. if you’re using GraphQL, the mutation might not support the delete directly. stick to REST for the teardown. GraphQL is better for reading state or updating small fields. mixing them in a single flow can get messy with auth tokens and caching. keep it simple. rest for delete, rest for patch. done.
patching the participant is the right path, but doing it sequentially in a gateway adds latency. i usually handle this in go by firing the wrap-up update and the conversation termination concurrently, since the termination is idempotent enough for our use case.
here’s the pattern i use with platformclientv2. you don’t need to wait for the patch to fully resolve before deleting, just ensure the http request is sent.
// update wrap-up code
wrapUpdate := platformclientv2.Conversationparticipant{
WrapUpCode: &platformclientv2.Wrappupcode{ Id: &codeID },
}
_, err := client.ConversationsApi.ConversationsParticipantPatch(conversationId, participantId, wrapUpdate)
// delete conversation immediately after
if err == nil {
client.ConversationsApi.ConversationsWebmessagingConversationDelete(conversationId)
}
it’s risky if the patch fails silently, so i wrap it in a retry middleware. if the patch fails, the conversation still ends but without the code. trade-off for speed. make sure your oauth token has conversation:write scope.
1 Like