OAuth “invalid_grant” on Refresh Token – API_ONLY/OAuth-In App via CLI

I am implementing OAuth 2.0 Authorization Code Grant for my API_ONLY/OAuth-In App created via the SmartThings CLI. The initial OAuth flow works: my server receives the authorization code, exchanges it for an access token and a refresh token, and I can use the access token to make successful API calls.

The problem:
Whenever my server attempts to use the refresh token to obtain a new access token (using the correct client_id, client_secret, and redirect_uri), the SmartThings token endpoint always responds with HTTP 401 and the following error:

  • {“error”:“invalid_grant”,“error_description”:“Invalid refresh token: ”}

  • This happens immediately, even on the first refresh attempt after the initial token exchange.

  • I have confirmed that my code always uses the latest refresh token provided by SmartThings.

  • The access token works for API calls until it expires; only the refresh fails.

What I’ve tried:

  • Repeated the entire OAuth flow with new apps and new tokens.

  • Verified that all parameters (client_id, client_secret, redirect_uri, refresh_token) are correct and match the app registration.

  • The issue is consistent and reproducible.

Questions:

  • Is there an additional approval, publishing, or enablement step required for API_ONLY/OAuth-In apps to support refresh tokens?

  • Is this a known issue or limitation for new API_ONLY/OAuth-In apps created via the CLI?

  • Is there a workaround or recommended process to enable refresh token support for new integrations?

Any guidance or help would be greatly appreciated, as this is blocking my integration.

Thank you,

Randy

In ‘curl’ terms the API call is:

curl -u "0817f4d5-d764-4d09-a741-d5da52889b6c:9da0e5f8-80c9-4f35-a59f-16ba88774f62" https://api.smartthings.com/oauth/token -d "grant_type=refresh_token&client_id=0817f4d5-d764-4d09-a741-d5da52889b6c&refresh_token=d4b93907-6bf8-46f0-8d14-ed007d5712c0"

where the client ID and secret are the username and password. Does that match with what you are sending?

Thank you for your response. I was able to confirm using that curl command worked. Then I found the difference in my php code was that I had the client_id and client_secret as parameters instead of the auth. Once I moved them to the auth instead of the params, it’s now working.

Thanks again!

-Randy