Recording API: PATCH to stop recording returns 409 Conflict

We are trying to programmatically stop a live call recording using PATCH /api/v2/recordings/{recordingId}. The request body contains the standard action payload with the stop command.

The endpoint responds with a 409 Conflict error indicating the recording is not in a startable state. We have verified the recording ID is correct and the status is currently ‘started’.

The code snippet for the request is as follows:

fetch(url, {
 method: 'PATCH',
 headers: { 'Authorization': `Bearer ${token}` },
 body: JSON.stringify({ action: 'stop' })
});

The 409 usually means the backend thinks the state is already changing or the ID you’re passing isn’t actually a live session recording ID. It’s a common gotcha with the recordings API because recordingId in the PATCH endpoint expects the specific session recording UUID, not the general conversation ID or a finalized recording ID.

Here’s what to check:

  • Verify the ID type: Make sure you’re grabbing the id from the recordings array in the conversation object, specifically where state is started. If you’re using a recordingId that was returned after the call ended, it won’t work for a stop action.
  • Check for race conditions: If you’re triggering this from a webhook that fires on conversation:updated, there might be a slight delay. The backend might still be processing the “start” event. A small retry with exponential backoff often fixes this if it’s a timing issue.
  • Use the Conversation API instead: Sometimes it’s cleaner to use PATCH /api/v2/conversations/{conversationId} with action: "stopRecording". This targets the conversation context directly and might handle state transitions more gracefully than hitting the recordings endpoint directly.

Here’s a quick Node.js snippet using the SDK to stop via the conversation, which tends to be more reliable:

const platformClient = require('genesys-cloud-platform-client');

const stopRecording = async (conversationId) => {
 const authSettings = {
 clientId: process.env.GC_CLIENT_ID,
 clientSecret: process.env.GC_CLIENT_SECRET
 };
 
 const oauthClient = new platformClient.AuthClient(authSettings);
 await oauthClient.loginClientCredentials();
 
 const conversationApi = new platformClient.ConversationsApi(oauthClient);
 
 try {
 // This stops all active recordings on the conversation
 await conversationApi.patchConversation(
 conversationId, 
 { action: 'stopRecording' }
 );
 console.log('Recording stopped successfully');
 } catch (error) {
 console.error('Failed to stop recording:', error.response?.data || error.message);
 }
};

If you definitely need to use the recordings endpoint, double-check the state field in the GET request for that specific recording ID before attempting the PATCH. The API is strict about the state machine.