Skip to main content

Token Lifetimes

OAuth 2.0 recommends using a combination of access tokens and refresh tokens to grant access to protected resources.

When the service issues a user token, a refresh token is also returned in that response. The refresh token can be used to obtain a new user token when the current user token expires.


Unlike user tokens, client tokens don't have a refresh token.

Queries to the Stitch API with an expired client or user token will yield the following response:

"errors": [
"message": "UNAUTHENTICATED: Token is expired or malformed"
"extensions": {

Client Token Lifetime

Client tokens are configured to have a 1-hour lifetime by default. The token expiry is indicated by the expires_in field (displayed in seconds) which is returned when successfully retrieving a client token. If the current client token has expired, you can request a new one by following the steps in the client token guide.

User Token Lifetime

User tokens are configured to have a 15-minute lifetime by default. The token expiry is indicated by the expires_in field (displayed in seconds) which is returned when successfully retrieving a user token. You can refresh your token before the indicated time to ensure you always have a fresh token.


If a user token is successfully refreshed but unused for a year the next request made will receive a reauthorization error.

Refresh Tokens

Refresh token expiry and lifetime

Refresh tokens have a sliding expiry of 365 days. Exact values for this duration can be found in your client credentials JSON as the key slidingRefreshTokenLifetime

  • A refresh token can only be used once. The result of the token call will include a new access and refresh token.
  • Requesting a new token also creates a new banking session. Depending on the bank, this means that it may trigger a login notification, or bring up a two-factor authentication prompt. The latter case will not interrupt the process of retrieving a new access token, but follow-up requests to the API may require user interaction.
  • A refresh token is only returned if it is explicitly requested by passing in offline_access as the scope.
  • Your client_secret needs to passed in when making a request to the token endpoint.

Refreshing the Token Using cURL

The below example bash script uses cURL to retrieve the user access and refresh tokens.

You'll need to replace the clientId and refreshToken with the appropriate values.

If correctly formed, the script will return a JSON payload with the token once you execute it.

clientSecret='<your client secret>'

curl -X POST \ \
-H 'Content-Type: application/x-www-form-urlencoded' \
-d "grant_type=refresh_token&client_id=$clientId&refresh_token=$refreshToken&client_secret=$clientSecret='<your client secret>'

Refreshing the Token Using JavaScript

The example Javascript function below uses fetch to retrieve the client access token. You'll need to pass in appropriate values for clientId, refreshToken and clientSecret to the function retrieveTokenUsingRefreshToken

async function retrieveTokenUsingRefreshToken(
) {
const body = {
grant_type: "refresh_token",
client_id: clientId,
refresh_token: refreshToken,
client_secret: clientSecret,
const bodyString = Object.entries(body)
.map(([k, v]) => `${k}=${encodeURIComponent(v)}`)

const response = await fetch("", {
method: "post",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: bodyString,

const responseBody = await response.json();
console.log("Token Response: ", responseBody);
return responseBody;

Refreshing the Token Using Postman

To get started, first import this Postman collection into your local Postman collections.

The second request in the collection is "Refresh Token". Replace the entries in the Body tab with the appropriate values and click send. The request will return a JSON payload with the token if correctly formed.

Response Body

A typical response body returned from the refresh token endpoint will look like the following:

"id_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6Im9TbWt2RmhqVWNia0I4MjRrek5fWkEiLCJ0eXAiOiJKV1QifQ.eyJuYmYiOjE1NzYyMjM1MjEsImV4cCI6MTU3NjIyMzgyMSwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo1MDAxIiwiYXVkIjoiZjVjYzk0ZWUtZTk1MS00MGQzLWEzMWUtNzk0NjU5ODA2MjMwIiwiaWF0IjoxNTc2MjIzNTIxLCJhdF9oYXNoIjoiZmNaeVN5MnBSYlBKMnUwSW1OWUNlQSIsInN1YiI6InVzZXIvZjVjYzk0ZWUtZTk1MS00MGQzLWEzMWUtNzk0NjU5ODA2MjMwL2ZuYi9kY2QxMzczNy0xMTc4LTRhMTUtOGQwYy00MGE0YzBiMmU1ODkiLCJhdXRoX3RpbWUiOjE1NzYyMjM0MTAsImlkcCI6ImxvY2FsIiwiYW1yIjpbInB3ZCJdfQ.ISRH-W9SgDJmTAcAFcrRggDb4Ym-0xmZ-Lbv1gfceUr00wjV8eiZcf_EE1Ca9t7fXgeFMsWTclc-ZxX5szWQAvLaqGdFo-3IlKuPgmftTmfTAb1y7_RWNIjuTjDtJvWzLnf1WGO62Ki_uz2kB3VicneDEzSx5YJRGJz6tZ5LBvrCAj_WQ4mpodNEawuC1komJMhROVtLdM7tAAE7BPhF3Ks0v-SiAzh8QtxBAs8l-13dcmgeXrgCiND5i520QtuOhdVV7DNQ1BvP8SGxMhmuA4V5s3V2BDTIUkR8_IsMq6OE-BxjXfflM1QGlV7_tRs52w5CLxfuFNX_sVIHNIWQzw",
"access_token": "ey-K-qPC5Cs0ERd0eDuLJI636rLEnd3Kwi5L1NrVNJY",
"expires_in": 900,
"token_type": "Bearer",
"refresh_token": "Wce1_ujqCD8B-BHAzrV-1S_3WFsHSxXKWUHGtfJvZvc",
"scope": "openid accounts transactions offline_access"
id_tokenThis is the same token as the one returned from the authorize call. id_token contains user profile information and is JWT encoded
access_tokenThe token needed to query the Stitch API
expires_inThe number of seconds until the token expires
refresh_tokenThe next refresh_token which needs to be stored for later use
scopeThe scopes that were granted by the user

Attempting to refresh the access token with an expired or malformed refresh token will yield the following response:

"error": "invalid_grant"