Python requests 401 on Genesys Cloud OAuth2 Client Credentials

What is the standard approach to authenticate using OAuth2 Client Credentials and get an access token with Python requests? I am building a CI validation script for my Terraform module to verify API access before applying changes. My current implementation sends a POST to https://api.mypurecloud.com/oauth/token with grant_type=client_credentials and a Basic Auth header derived from my client ID and secret, but I consistently receive a 401 Unauthorized response. The environment variables are confirmed correct in the pipeline logs. Here is the snippet causing the failure:

import requests
import base64

client_id = "my-client-id"
client_secret = "my-client-secret"
credentials = f"{client_id}:{client_secret}"
encoded_credentials = base64.b64encode(credentials.encode()).decode()

headers = {
 "Authorization": f"Basic {encoded_credentials}",
 "Content-Type": "application/x-www-form-urlencoded"
}

response = requests.post(
 "https://api.mypurecloud.com/oauth/token",
 headers=headers,
 data={"grant_type": "client_credentials"}
)
print(response.status_code)
print(response.text)

Is there a specific header requirement or URL path variation for the Genesys Cloud OAuth endpoint that I am missing?

You might want to check at the exact structure of the Basic Auth header. It’s a common gotcha that Python’s requests library doesn’t automatically encode the client ID and secret into Base64 for the Authorization header like curl does. You need to explicitly handle that encoding. Here is a working snippet using base64 and urllib.parse to construct the correct payload and headers.

import requests
import base64
import urllib.parse

client_id = "your_client_id"
client_secret = "your_client_secret"

# Encode credentials for Basic Auth
credentials = f"{client_id}:{client_secret}"
encoded_credentials = base64.b64encode(credentials.encode('utf-8')).decode('utf-8')

headers = {
 "Authorization": f"Basic {encoded_credentials}",
 "Content-Type": "application/x-www-form-urlencoded"
}

# Prepare the body exactly as expected by the OAuth endpoint
body = urllib.parse.urlencode({
 "grant_type": "client_credentials",
 "scope": "admin:api read:users" # Add your required scopes here
})

response = requests.post(
 "https://api.mypurecloud.com/oauth/token",
 headers=headers,
 data=body
)

print(response.status_code)
print(response.json())

Make sure your scopes in the body match what your Terraform module actually needs. If you get a 403 after fixing the 401, check the scope permissions in the Admin portal. This approach mirrors how I handle auth in my CI/CD pipelines before running terraform plan.