// Using the platform SDK to unlock a flow
import { FlowsApi } from '@genesys/purecloudplatform-client-v2';
const flowsApi = new FlowsApi();
try {
// The flowId must be passed as a query parameter, not in the body
await flowsApi.postFlowsActionsUnlock('flow-id-here');
console.log('Flow unlocked successfully');
} catch (e) {
console.error('Unlock failed:', e.response?.status);
}
The 403 usually boils down to a mismatch between the role’s permission and the actual state of the flow. In an org our size - we’ve got about 1,200 agents - we’ve seen this happen when the service account has the “flow” permission but lacks the specific “flow.unlock” grant. It’s a common trap because the UI makes it look like one general permission.
Another thing is the request structure. The /api/v2/flows/actions/unlock endpoint doesn’t accept a JSON body. If the code is sending the flow ID in the payload instead of as a query parameter, the gateway can sometimes throw a 403 or 400 depending on how the request is framed. The SDK handles the query string automatically if you pass the ID as the first argument.
Right, so the permissions are “correct” but the API is still throwing a 403. Classic. Why does it do this? Because the unlock endpoint is incredibly picky about the flow’s current state. If the flow is already unlocked or if there’s a ghost session hanging around from a crashed Architect window, the API just gives up and returns a forbidden error instead of a helpful “it’s already unlocked” message. Ffs.
The fix is to wrap the call in a check. Use a script to verify the status before trying to force the unlock, otherwise you’re just shouting into a void. Argh. If you’re using Python, just hammer the endpoint with a try-except block but log the actual response body to see if it’s a locking conflict.
import requests
# The flow ID is passed as a query param, not the body.
# Because of course it is.
url = f"https://api.mypurecloud.com/api/v2/flows/actions/unlock?flow={flow_id}"
headers = {"Authorization": f"Bearer {token}"}
response = requests.post(url, headers=headers)
if response.status_code == 403:
# It's likely already unlocked or stuck in a state
# that the API can't describe properly.
print("403 received - likely a state conflict.")
# Check if the flow is already checked in to avoid the 403
import PureCloudPlatformClientV2
api = PureCloudPlatformClientV2.FlowsApi()
# Logic to verify current state before calling /api/v2/flows/actions/unlock
First, what are the data retention policies for your flow version history? If you’re archiving old versions for compliance, that’s a separate concern, but for this 403, the earlier reply is spot on. The system won’t let you unlock what isn’t locked. It’s a state-machine conflict, not a permission failure.
Error
Cause
Resolution
403 Forbidden
Flow already unlocked
Skip unlock call
403 Forbidden
Insufficient Role
Verify flow:unlock
Just a heads up: if you’re automating this as part of a wider quality process, watch out for evaluation form versioning pitfalls. Changing a flow that impacts how interactions are recorded can break your QM form mapping if the versions don’t align.
this usually happens because someone left the flow open in the UI. if a user has the flow editor open, it creates a soft lock that the API can’t always override with a standard unlock call, even if the service account has the right permissions. it’s a weird quirk where the session lock takes priority over the role.
the workaround we’ve used is to try a check-in first to clear any pending changes before hitting the unlock. it doesn’t always work if the session is totally hung, but it’s worth a shot.
try hitting this first: POST /api/v2/flows/actions/checkin?flow={flowId}
if that returns a 200, then run your unlock call again. if it still 403s, you’ll probably have to hunt down who’s actually in the flow editor and have them close the tab. once the session expires or the user leaves, the /api/v2/flows/actions/unlock call should go through without a hitch.