Backend API call to close Web Messaging session returns 401 Unauthorized

Getting a 401 Unauthorized error when trying to close a web messaging session from our backend service. The goal is to programmatically end the conversation once the agent has resolved the issue, without waiting for the guest to hang up. We need this for accurate WFM adherence tracking on our side.

I’ve been using the Genesys Cloud API documentation for the Conversations API. Specifically, I’m looking at the endpoint POST /api/v2/conversations/messaging/{conversationId}/sessions/{sessionId}/close. The docs say this requires a valid OAuth token with the conversations:write scope.

Here is the Python snippet I’m using to make the request:

import requests
import json

def close_session(access_token, conversation_id, session_id):
 url = f"https://api.mypurecloud.com/api/v2/conversations/messaging/{conversation_id}/sessions/{session_id}/close"
 headers = {
 'Authorization': f'Bearer {access_token}',
 'Content-Type': 'application/json'
 }
 # The payload is empty for a POST close request based on examples
 payload = {}
 
 response = requests.post(url, headers=headers, json=payload)
 return response.status_code, response.text

# Usage
status, body = close_session('my_valid_token', 'conv-123', 'sess-456')
print(status, body)

The error response body looks like this:

{"error_code":"invalid_grant","error":"invalid_grant","message":"The access token provided is invalid or has expired."}

I know the token isn’t expired because I can use the same token to fetch conversation details via GET /api/v2/conversations/messaging/{conversationId} without any issues. The scope conversations:write is definitely included in the client credentials flow I’m using to generate the token. I even tried regenerating the token right before the call.

Is there a specific permission or setting in the Genesys Cloud admin portal that blocks backend-initiated session closures? Or am I hitting the wrong endpoint? I’ve checked the webhook events and the session still shows as ‘active’ in the UI after my script runs.

Any help would be appreciated. I’ve been stuck on this for two days.