Invalid Refresh Token Error – Assistance Needed

I am encountering an issue when attempting to request an access token using a refresh token. The error message returned is as follows:
{"error"=>"invalid_grant", "error_description"=>"The refresh token was not valid."}

Our refresh token has not expired, but we are still receiving the "invalid token" response. As a temporary solution, we are reauthorizing the organization to get a new token.

Could anyone please assist in identifying the cause of this issue and recommend a resolution? Please let me know if anything is needed.

Thank you for your help!

Comments

  • Alex Wong
    Alex Wong Community All-Star
    Ninth Anniversary Kudos 5 Facilitator 3 Raiser's Edge NXT Fall 2025 Product Update Briefing Badge

    @GoKid Mobi
    going to need to know how you are calling the refresh token and all the details on the input/output of your refresh api call.

  • Hi @Alex Wong, Thanks for response.

    This is how I'm trying to get the refresh token using Ruby. Before, it was working.

    url = 'https://oauth2.sky.blackbaud.com/token'

    encoded_credentials = Base64.strict_encode64(Blackbaud_Client_ID:Blackbaud_Client_Secret)

    headers = {

    "Authorization" => "Basic #{encoded_credentials}",

    "Content-Type" => "application/x-www-form-urlencoded"

    }

    body ={

    grant_type: 'refresh_token',

    refresh_token: blackbaud_refresh_token

    }

    HTTParty.post(url, headers: headers, body: URI.encode_www_form(body))

    Now, when I hit the API, I'm getting this error.

    => #<HTTParty::Response:0x1ad10 parsed_response={"error"=>"invalid_grant", "error_description"=>"The refresh token was not valid."}, @response=#&lt;Net::HTTPBadRequest 400 Bad Request readbody=true>, @headers={"date"=&gt;["Mon, 14 Apr 2025 06:27:19 GMT"], "content-type"=>["application/json; charset=utf-8"], "content-length"=>["80"], "connection"=>["close"], "cache-control"=>["no-store,no-cache"], "pragma"=>["no-cache"], "request-context"=>["appId=APP_id"], "x-content-type-options"=>["nosniff"], "strict-transport-security"=>["max-age=31536000; includeSubDomains"], "x-azure-ref"=>["Reference_code"], "x-cache"=>["CONFIG_NOCACHE"]}>

  • Alex Wong
    Alex Wong Community All-Star
    Ninth Anniversary Kudos 5 Facilitator 3 Raiser's Edge NXT Fall 2025 Product Update Briefing Badge

    @GoKid Mobi
    I don't believe you understand the Authroization flow of Blackbaud SKY API, best you read it up

    https://developer.blackbaud.com/skyapi/docs/authorization

    To use the token API to refresh and obtain new authorization token (which is needed to make SKY API calls), you first need a refresh token, that is not a encoded/calcuated from client end. This refresh token comes from Blackbaud through OAuth2 to begin with, where user must authenticate first. From looking at your code, you are providing the authorization header that comes from base64 encoding of client_id and client_secret of your application. this will not work.