Refreshing NICE CXone API token in Azure DevOps pipeline without hardcoding secrets

Running into a wall with token rotation in our CI/CD pipeline. We’re using the NICE CXone REST API to push updated flow configurations from our repo, but the default OAuth2 bearer tokens expire after an hour. The pipeline runs overnight, so it fails halfway through with a 401 Unauthorized.

We’re using a service account with the cxone:flows:write scope. Here’s the current setup in the azure-pipelines.yml:

- task: Bash@3
 displayName: 'Get Auth Token'
 inputs:
 targetType: 'inline'
 script: |
 TOKEN=$(curl -s -X POST https://login.nicecxone.com/oauth/token \
 -H 'Content-Type: application/x-www-form-urlencoded' \
 -d 'grant_type=client_credentials&client_id=$(CXONE_CLIENT_ID)&client_secret=$(CXONE_CLIENT_SECRET)')
 echo "##vso[task.setvariable variable=BEARER_TOKEN;issecret=true]$TOKEN"

- task: Bash@3
 displayName: 'Deploy Flow'
 inputs:
 targetType: 'inline'
 script: |
 curl -X PUT https://api.nicecxone.com/api/v2/flows/$(FLOW_ID) \
 -H 'Authorization: Bearer $(BEARER_TOKEN)' \
 -H 'Content-Type: application/json' \
 -d @flow.json

The first task grabs the token. The second task uses it. If the pipeline takes longer than 3600 seconds, the second task crashes. I tried adding a refresh step, but CXone doesn’t seem to support the refresh_token grant type for client_credentials. It just gives me a new access token and a new refresh token that I can’t use.

Is there a way to generate a long-lived token specifically for CI/CD? Or should I just loop the curl command to check expiration and re-auth? That feels messy. Also, the client_secret is stored in Azure Key Vault, so rotation is handled there, but the token lifespan is the bottleneck.

Anyone got a clean pattern for this?

The docs state: “The client credentials grant type is ideal for machine-to-machine communication.” You’re probably not hitting the token endpoint correctly. Check your client_secret encoding in the pipeline variable group.