Developer Center
Getting Started
Using Jobber’s API
Building Your App
Publishing Your App
App Template Project
Custom Integrations
Changelog

Refresh Token Rotation

Refresh Token Rotation is a setting on your app in the Developer Center that controls whether a new refresh token is issued each time a refresh token is used to obtain a new access token.

Refresh Token Rotation is enabled by default and must remain enabled for apps published in the Jobber App Marketplace. It can be disabled during development, but must be re-enabled before submitting for review.

To check whether Refresh Token Rotation is enabled on your app, go to Apps in the Developer Center and view the app's settings. The setting can be toggled when creating or editing an app.

How it works

When your app calls the refresh token endpoint to obtain a new access token, the response includes both a new access token and a refresh token.

  • With Rotation ON: the refresh token in the response is a new, unique token. The previous refresh token is immediately invalidated and must be discarded. Always overwrite the stored refresh token with the new one.
  • With Rotation OFF: the same refresh token is returned each time. A warning is also included in the response:
{
  "warning": "Refresh token rotation is off. This setting is required for apps to be published in the Jobber App Marketplace. You can turn it on in the Developer Center. https://developer.getjobber.com/apps",
  "access_token": "<ACCESS_TOKEN>",
  "refresh_token": "<REFRESH_TOKEN>"
}

Implementation

The critical requirement is that every time a refresh token is used, both the new access token and the new refresh token are saved to your token store before making any further API calls. The following example illustrates the recommended pattern:

async function refreshAccessToken() {
  // Load current tokens
  const accountTokens = {
    tokenType: "bearer",
    accessToken: account.jobberAccessToken,
    refreshToken: account.jobberRefreshToken,
  };

  // Request new tokens from Jobber
  // Same refresh token (if Refresh Token Rotation is OFF),
  // New refresh token (if Refresh Token Rotation is ON)
  const tokens = await oauth2RefreshAccessToken(accountTokens);

  // Immediately overwrite the stored tokens with the new ones
  await account.updateJobberTokens(tokens);
}

The key principle is to always update stored tokens before using the new access token for anything else.

Using an invalidated refresh token

What happens when an old (already rotated) refresh token is used depends on when your app was created.

Apps created before January 2, 2024: Jobber detects the attempt and returns a warning alongside new tokens. If your response includes a warning field, log it. It signals a configuration or implementation issue that should be addressed.

{
  "warning": "Unexpected Refresh Token Redemption: A newer refresh token exists than the one that was used for token redemption on this request. Please check your app's handling of refresh token rotation. https://developer.getjobber.com/docs/building_your_app/refresh_token_rotation",
  "access_token": "<ACCESS_TOKEN>",
  "refresh_token": "<REFRESH_TOKEN>"
}

Apps created after January 2, 2024: Jobber cannot detect the attempt. The request simply fails with:

Error: The provided refresh token is not valid.

In both cases, if you are seeing these errors in production, the root cause is almost always one of the two implementation issues described below.

Common implementation issues

Stale token variables

After refreshing tokens, always reload them from your token store before making further API calls. If tokens are loaded into a variable at the start of a request and that variable is used after a mid-request refresh, subsequent calls will fail because the variable still holds the invalidated tokens. Update the variable immediately after saving the new tokens, or reload from your token store before each use.

Concurrent token refresh

Before refreshing tokens, check whether the access token in your token store still matches the one your process loaded. If it has changed, another process has already refreshed - skip the refresh and reload the latest tokens instead. This reduces the risk of simultaneous refreshes, though it doesn't fully eliminate it if two processes load the tokens at the same time. If concurrent refresh attempts are a concern in your environment, consider implementing a coordination mechanism so only one process refreshes at a time.

Note that these two scenarios can also occur in combination, further complicating the situation.