{"templateId":"markdown","sharedDataIds":{"sidebar":"sidebar-guides/developer/auth-and-security/sidebars.yaml","oas-api-reference/index.yaml":"oas-api-reference/index.yaml"},"props":{"metadata":{"markdoc":{"tagList":["sub-header","tabs","tab","openapi-response-sample","admonition","glossary"]},"type":"markdown"},"seo":{"title":"Refresh tokens","siteUrl":"https://docs.wise.com","projectTitle":"Wise Platform","description":"Obtain new user access tokens without requiring your user to reauthorize, maintaining long-term access.","llmstxt":{"hide":false,"sections":[{"title":"Table of contents","includeFiles":["**/*"],"excludeFiles":[]}],"excludeFiles":[]}},"dynamicMarkdocComponents":["openapi"],"compilationErrors":[],"ast":{"$$mdtype":"Tag","name":"article","attributes":{},"children":[{"$$mdtype":"Tag","name":"Heading","attributes":{"level":1,"id":"refresh-tokens","__idx":0},"children":["Refresh tokens"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"span","attributes":{"class":"sub-header"},"children":["Learn about generating new user access tokens. "]}]},{"$$mdtype":"Tag","name":"hr","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["A refresh token lets your application obtain new user access tokens without requiring the user to re-authorise. Refresh tokens are valid for up to 20 years, enabling long-term access to user accounts."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"using-a-refresh-token","__idx":1},"children":["Using a refresh token"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["You’ll receive a refresh token in the same response as the ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/guides/developer/auth-and-security/user-access-token"},"children":["user access token"]},"."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["User access tokens are valid for ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["12 hours"]}," by default. Use the refresh token to generate a new user access token before the token expires (Wise recommends a ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["6 hour"]}," refresh cadence)."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Make a ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/api-reference/oauth-token/oauthtokencreate"},"children":["create an OAuth token request"]}," with ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["grant_type"]}," set to ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["refresh_token"]},"."]},{"$$mdtype":"Tag","name":"Tabs","attributes":{"size":"medium"},"children":[{"$$mdtype":"Tag","name":"TabItemFragment","attributes":{"label":"Request","disable":false},"children":[{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"bash","header":{"controls":{"copy":{}}},"source":"curl -X POST https://api.wise.com/oauth/token \\\n  -u '<client_id>:<client_secret>' \\\n  -d 'grant_type=refresh_token' \\\n  -d 'refresh_token=<REFRESH_TOKEN>'\n","lang":"bash"},"children":[]}]},{"$$mdtype":"Tag","name":"TabItemFragment","attributes":{"label":"Response","disable":false},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The response returns the ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/api-reference/oauth-token/oauthtokencreate"},"children":["OAuth token response"]}," with a new access token."]},{"$$mdtype":"Tag","name":"OpenApiResponseSample","attributes":{"descriptionFile":"oas-api-reference/index.yaml","operationId":"oauthTokenCreate","responseSamplesResolved":[{"lang":"json","title":"200 application/json","source":"{\n  \"access_token\": \"01234567-89ab-cdef-0123-456789abcdef\",\n  \"token_type\": \"bearer\",\n  \"refresh_token\": \"01234567-89ab-cdef-0123-456789abcdef\",\n  \"expires_in\": 43199,\n  \"expires_at\": \"2025-04-11T03:43:28.148Z\",\n  \"refresh_token_expires_in\": 628639555,\n  \"refresh_token_expires_at\": \"2045-03-12T13:49:23.552Z\",\n  \"scope\": \"transfers\",\n  \"created_at\": \"2020-01-01T12:33:33.12345Z\"\n}"},{"lang":"json","title":"400 application/json","source":"{\n  \"error\": \"invalid_request\",\n  \"error_description\": \"Missing grant type\"\n}"},{"lang":"json","title":"401 application/json","source":"{\n  \"error\": \"invalid_grant\",\n  \"error_description\": \"Invalid user credentials.\"\n}"},{"lang":"json","title":"429 application/json","source":"{}"}]},"children":[]}]}]},{"$$mdtype":"Tag","name":"Admonition","attributes":{"type":"warning","name":"Only one user access token is valid at a time!"},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":["When you use a refresh token to generate a new access token, the ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["previous access token is immediately invalidated"]},"."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Your application must switch to the new token for all subsequent API calls. Any in-flight requests using the old token will receive a ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["401 invalid_token"]}," error."]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"managing-refresh-tokens","__idx":2},"children":["Managing refresh tokens"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Encrypt refresh tokens at rest as they grant long-term access."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Use centralised storage and avoid multiple services refreshing simultaneously."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Monitor for anomalies as unusual refresh patterns may indicate compromise."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["If you suspect a token is compromised, prompt the user to re-authorise (for partners using the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["authorization_code"]}," grant) or request a new user access and refresh token pair (for partners using the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["registration_code"]}," grant flow)."]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"invalid-refresh-tokens","__idx":3},"children":["Invalid refresh tokens"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["An invalid token returns the error ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["invalid_grant"]},". A refresh token can be invalidated before its 20-year expiration for any of the following reasons:"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["User revokes your application's access."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["User enables enhanced security on their account."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Wise revokes token due to security concerns."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Token validity period expires (if configured shorter than the default value)."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["A new refresh token is generated via a registration code"]}," — this immediately"," ","invalidates the previous refresh token for that user"]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"recovery-for-user-authorised-partners","__idx":4},"children":["Recovery for user-authorised partners"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["For ",{"$$mdtype":"Tag","name":"Glossary","attributes":{"term":"Embedded finance","def":"The term used to refer to regulated financial institutions and fintech platforms who offer Wise services to their customers by embedding Wise features and capabilities directly into their applications. Sometimes called Embedded partners or Embedded integrations."},"children":["embedded finance"]}," partners, or other partners that use the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["authorization_code"]}," grant type."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["When a refresh token becomes invalid, your application should:"]},{"$$mdtype":"Tag","name":"ol","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Detect the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["invalid_grant"]}," error from the token response."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Clear the stored access token and refresh token for that user."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Redirect the user through the OAuth authorisation flow to obtain a new authorisation code, then exchange it for a new token pair."]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"recovery-for-partners-using-registration-code-flow","__idx":5},"children":["Recovery for partners using registration code flow"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Correspondent and enterprise partners typically don't have a user-facing OAuth redirect flow. If your refresh token is invalidated:"]},{"$$mdtype":"Tag","name":"ol","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Detect the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["invalid_grant"]}," error from the token response."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Clear the stored tokens for that user or profile."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Use your ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["registration code"]}," to generate a new token pair via ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["POST /oauth/token"]}," with ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["grant_type=registration_code"]},". The registration code is your primary recovery mechanism — store it securely!",{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["If the registration code is also invalid (for example, because the user reclaimed the account), contact ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"mailto:api@wise.com"},"children":["api@wise.com"]}," for assistance."]}]}]}]},{"$$mdtype":"Tag","name":"Admonition","attributes":{"type":"info","name":"Registration code behaviour"},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Exchanging a registration code generates a new refresh token and immediately invalidates any previous refresh token for that user. Ensure your application updates its stored tokens immediately after the exchange."]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"avoiding-concurrent-refresh-issues","__idx":6},"children":["Avoiding concurrent refresh issues"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["A common cause of token errors is multiple services or instances refreshing the same token simultaneously. When two concurrent refresh requests are made:"]},{"$$mdtype":"Tag","name":"ol","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Both requests succeed and return different access tokens."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Only the ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["last"]}," token issued is valid — the first is immediately invalidated."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Any service using the first token receives ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["401 invalid_token"]}," errors."]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["To prevent situations where multiple services invalidate each other's tokens:"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Centralise token management"]},": Use a single service or process responsible for refreshing tokens and distributing the current valid token to other services."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Never refresh in parallel"]},": If two refresh requests execute simultaneously, both will succeed, but only the token from the second response will be valid. The first token will immediately return ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["401 invalid_token"]},"."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Use locking or queuing"]},": Implement a mutex or queue around your refresh logic to ensure only one refresh request is in-flight at any time."]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"refresh-token-errors","__idx":7},"children":["Refresh token errors"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["These errors can occur when calling ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["POST /oauth/token"]}," with ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["grant_type=refresh_token"]},":"]},{"$$mdtype":"Tag","name":"div","attributes":{"className":"md-table-wrapper"},"children":[{"$$mdtype":"Tag","name":"table","attributes":{"className":"md"},"children":[{"$$mdtype":"Tag","name":"thead","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"HTTP status"},"children":["HTTP status"]},{"$$mdtype":"Tag","name":"th","attributes":{"width":"20%","data-label":"Error code"},"children":["Error code "]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Cause"},"children":["Cause"]}]}]},{"$$mdtype":"Tag","name":"tbody","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["400"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["invalid_grant"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["The refresh token is expired, revoked, or not recognised."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["401"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["invalid_client"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Your ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["client_id"]}," or ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["client_secret"]}," is incorrect, or the Basic Auth header is malformed."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["400"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["invalid_request"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["The ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["grant_type"]}," parameter is missing from the request body."]}]}]}]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Example ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["invalid_grant"]}," response:"]}]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"json","header":{"controls":{"copy":{}}},"source":"{\n  \"error\": \"invalid_grant\",\n  \"error_description\": \"Invalid refresh token (expired or revoked)\"\n}\n","lang":"json"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"errors-after-refresh","__idx":8},"children":["Errors after refresh"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["If your requests fail with ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["401 invalid_token"]}," immediately after a successful refresh:"]},{"$$mdtype":"Tag","name":"ol","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Check for concurrent refresh requests"]},". Another service may have refreshed the token after you did, invalidating your token. Fetch the latest token from your central token store."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Confirm you're using the new token"]},". Verify your application updated its stored token and is not still using the previous access token."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Check the environment"]},". Sandbox tokens are not valid in production, and vice versa."]}]}]},"headings":[{"value":"Refresh tokens","id":"refresh-tokens","depth":1},{"value":"Using a refresh token","id":"using-a-refresh-token","depth":2},{"value":"Managing refresh tokens","id":"managing-refresh-tokens","depth":3},{"value":"Invalid refresh tokens","id":"invalid-refresh-tokens","depth":2},{"value":"Recovery for user-authorised partners","id":"recovery-for-user-authorised-partners","depth":3},{"value":"Recovery for partners using registration code flow","id":"recovery-for-partners-using-registration-code-flow","depth":3},{"value":"Avoiding concurrent refresh issues","id":"avoiding-concurrent-refresh-issues","depth":3},{"value":"Refresh token errors","id":"refresh-token-errors","depth":2},{"value":"Errors after refresh","id":"errors-after-refresh","depth":3}],"frontmatter":{"seo":{"description":"Obtain new user access tokens without requiring your user to reauthorize, maintaining long-term access.","title":"Refresh tokens"}},"lastModified":"2026-07-14T16:35:54.000Z","pagePropGetterError":{"message":"","name":""}},"slug":"/guides/developer/auth-and-security/refresh-tokens","userData":{"isAuthenticated":false,"teams":["anonymous"]},"isPublic":true}