Server-to-Server OAuth app always returns invalid_client when requesting access token

I am experiencing an issue with my Server-to-Server OAuth app.
Whenever I try to request an access token, the Zoom API always responds with:

{
  "reason": "Invalid client_id or client_secret",
  "error": "invalid_client"
}

What I have already tried

  • Created a brand new Server-to-Server OAuth app

  • Copied Account ID, Client ID, Client Secret directly from App Credentials page

  • Added scope: user:update:user:admin

  • Activated the app after adding scopes

  • Verified that I am using the correct endpoint:

    POST https://zoom.us/oauth/token?grant_type=account_credentials&account_id={accountId}
    
    
  • Sent Authorization: Basic base64(ClientID:ClientSecret) in the header

  • Tested both with PowerShell and Python (same result)

  • Rebooted my computer, cleared browser cache/cookies, and regenerated the client secret multiple times

  • I am the account owner of this Zoom account

Example PowerShell snippet I used

$clientId = "xxxxxx"
$clientSecret = "yyyyyy"
$accountId = "zzzzzz"

$auth = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes("${clientId}:${clientSecret}"))
$headers = @{ Authorization = "Basic $auth" }
$uri = "https://zoom.us/oauth/token?grant_type=account_credentials&account_id=$accountId"

Invoke-RestMethod -Uri $uri -Headers $headers -Method Post

Result

Always returns:

400 Bad Request
{"reason":"Invalid client_id or client_secret","error":"invalid_client"}


Could you please check on the backend why my credentials are being rejected, even for a brand new Server-to-Server OAuth app?

Thank you very much for your help!

Could you move the query string into the request body and declare a Content-Type: application/x-www-form-urlencoded; charset=utf-8 header? Our application uses the request body and so do most of the Server-to-Server OAuth examples, including PowerShell. This is also better for privacy since the request URL tends to be recorded in log files.