Obtaining and refreshing an access token

Access token Refresh token OAuth Expiry

You call the ReceiptRoller API with the access token from the OAuth authorization code flow in an Authorization: Bearer … header. Access tokens last 8 hours, so you renew them with the refresh token. This article covers lifetimes, the requests for obtaining and refreshing tokens, how the refresh token is replaced on every refresh, and the cases in which tokens stop working. For the authorize URL and the consent screen, see the API Authentication Guide.

Lifetimes

Token Lifetime Used for
Access token8 hours (expires_in: 28800)Calling the API
Refresh token30 days from issueRefreshing the access token (works once)
Authorization code10 minutes (works once)Getting the first tokens

Obtain tokens (authorization code to tokens)

POST https://receiptroller.io/api/v1/auth/token
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code
&code=AUTHORIZATION_CODE
&redirect_uri=https%3A%2F%2Fexample.com%2Fcallback
&client_id=YOUR_CLIENT_ID
&client_secret=YOUR_CLIENT_SECRET

If you used PKCE, send code_verifier as well. Public clients send no client_secret and must send code_verifier.

Response

{
  "access_token": "…",
  "refresh_token": "…",
  "token_type": "Bearer",
  "expires_in": 28800,
  "scope": "store.products.read store.orders.read",
  "accessToken": "…",
  "refreshToken": "…",
  "tokenType": "Bearer",
  "expiresIn": 28800
}

The camelCase keys repeat the same values for older clients. New apps should read the snake_case keys.

Refresh (refresh token to new tokens)

POST https://receiptroller.io/api/v1/auth/token
Content-Type: application/x-www-form-urlencoded

grant_type=refresh_token
&refresh_token=REFRESH_TOKEN
&client_id=YOUR_CLIENT_ID
&client_secret=YOUR_CLIENT_SECRET

The response has the same shape as above. Public clients send no client_secret.

  • Every refresh returns a new access token and a new refresh token. Store both every time.
  • The refresh token you used, and the old access token paired with it, stop working at that moment, even if the old access token had time left.
  • The new refresh token is valid for 30 days from the refresh. As long as you refresh at least once every 30 days, you keep working without re-authorization.
  • Scopes and the bound business account stay the same as before the refresh.

When to refresh

  • Before expiry: work out the expiry from expires_in and refresh a little before it.
  • On a 401: when the API returns 401, refresh once and retry; if you still get 401, send the user through authorization again.

Refreshing twice with the same refresh token makes the second attempt fail with invalid_grant. If several processes share a token, refresh from one place, or use a lock so that two refreshes never run at once.

When tokens stop working

Case What happens
The refresh token's 30 days have passedRefresh returns 401 invalid_grant. Re-authorization is needed.
You refreshed with a refresh token that was already used401 invalid_grant. Use the most recent refresh token you received.
The business account's owner disconnected the app in "Connected apps"Every token the app holds for that business account is revoked: API calls return 401 and refresh returns invalid_grant.
Your app revoked it with /api/v1/auth/revokeThat access token and refresh token pair stops working.
The client secret was regeneratedIssued access tokens keep working until they expire, but refreshing with the old secret returns 401 invalid_client. Switch to the new secret.
The app was deletedAll of the app's tokens are deleted with it. Refresh returns 401 invalid_client.
The business account's plan dropped below StarterRefresh returns 401 invalid_grant, with an error_description saying the plan is needed. The tokens are not revoked, so after the plan is restored the same refresh token works again.

When re-authorization is needed, send the user to the authorize URL again.

Check a token

POST https://receiptroller.io/api/v1/auth/verify
Content-Type: application/json

{ "accessToken": "ACCESS_TOKEN" }

A valid token returns 200 with active, clientId, scope, expiresAt and organizationId; an invalid one returns 401. organizationId is the business account the token is bound to, or null for a token that is not bound to one.

Storage

  • Keep tokens on your server, not in browser local storage. In mobile apps, use the device's secure storage (such as the iOS Keychain).
  • Encrypt tokens you store in a database.
  • Store them per user and per business account so they cannot be mixed up.
  • Do not write them to logs.

Related guides

Published: 2026-04-27 Updated: 2026-10-26