OAuth client_credentials token expiring too fast for Azure DevOps pipeline

Running into an issue with token validity in our CI/CD pipeline. We’re using the client credentials flow to authenticate an Azure Function that pushes configuration changes via the Platform API. The token we get back expires in 3600 seconds. That’s way too short for a pipeline that might hang or retry.

Here’s the C# code using the official SDK:

var api = new OAuthApiClient();
var token = await api.PostOAuth2ClientCredentialsAsync(new PostOAuth2ClientCredentialsRequest 
{
 GrantType = "client_credentials",
 ClientId = env["GENESYS_CLIENT_ID"],
 ClientSecret = env["GENESYS_CLIENT_SECRET"]
});

The response looks fine, but the ExpiresIn is always 3600. I checked the docs for OAuth2 and it says: “The default expiration time for tokens is 1 hour.” I tried adding scope parameters, but that doesn’t change the lifetime.

Is there a way to request a longer-lived token programmatically? Or do we have to implement a refresh loop in the pipeline script? The pipeline agent doesn’t have a persistent session, so storing a refresh token is messy. Feels like I’m missing a config flag somewhere.

The SDK doesn’t expose a grant_type override for client_credentials, and Genesys Cloud strictly caps that scope at one hour. You’ll need to handle the refresh logic in your Azure Function instead of fighting the token lifetime. Since you’re already in a server-side environment, just cache the token and check the expires_in field before every API call. If it’s within, say, 300 seconds of expiration, fire off a new POST to /oauth2/token with your client_id and client_secret. It’s a bit of boilerplate, but it keeps your pipeline from choking on stale auth headers.

Here’s how that check looks in pure Node.js, which you can easily port to C#. Keep the token in a closure or a simple singleton so you don’t hammer the OAuth endpoint. This pattern works well for long-running processes where you can’t afford the overhead of re-authenticating every single request, yet you need to stay compliant with the security constraints. It’s not pretty, but it’s the only way to stay under the radar while keeping the connection alive.

let tokenData = null;

async function getToken() {
 if (tokenData && Date.now() < tokenData.exp - 300000) {
 return tokenData.access_token;
 }

 const res = await fetch('https://api.mypurecloud.com/oauth2/token', {
 method: 'POST',
 headers: { 'Content-Type': 'application/json' },
 body: JSON.stringify({ grant_type: 'client_credentials', client_id: process.env.CLIENT_ID, client_secret: process.env.CLIENT_SECRET })
 });
 
 tokenData = await res.json();
 tokenData.exp = Date.now() + (tokenData.expires_in * 1000);
 return tokenData.access_token;
}