Obtaining and refreshing an access token

Access token Refresh token OAuth Expiry
Who this article is for
For developers handling the access tokens used to call the ReceiptRoller API.

Call the API by attaching the access token obtained via the OAuth authorization code flow to the Authorization: Bearer ... header. The access token has an expiry; before it expires, refresh it with the refresh token.

Guideline expiries

Token Expiry Purpose
Access token1 hourAPI calls
Refresh token90 daysRefreshing the access token
Authorization code10 minutes (one-time)Initial token retrieval

Obtaining (authorization code → token)

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

grant_type=authorization_code
&code={authorization code}
&redirect_uri={registered URI}
&client_id={client ID}
&client_secret={client secret}

Response

{
  "access_token": "eyJhbGciOiJIUzI1NiIs...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "refresh_token": "rrrt_xxxxxxxxxxxx",
  "scope": "store.read receipt.read"
}

Refreshing (refresh token → new access token)

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

grant_type=refresh_token
&refresh_token={refresh token}
&client_id={client ID}
&client_secret={client secret}

The response contains a new access token and, in some cases, a new refresh token. If a new refresh token is returned, always save it and use it from then on (the old refresh token is invalidated).

Implementation patterns for refresh timing

  • Proactive refresh: automatically refresh 5 minutes before expiry (recommended)
  • Lazy refresh: refresh after receiving a 401 error and then retry
  • Proactive + lazy combined: primarily proactive, with lazy also implemented in case of unexpected failures

In a system where many concurrent requests run, either consolidate the token refresh to a single worker or use a distributed lock to prevent double refreshes.

Cases where it is invalidated

  • The refresh token's 90-day expiry passed
  • The user disconnected the integration
  • The secret was regenerated
  • The app was suspended by an administrator
  • There was no API access for a long time (30 days or more)

On invalidation, an invalid_grant error is returned. The app should guide the user to the re-authorization flow.

Storage

  • Store tokens on the server side only. Avoid local storage of frontends / mobile apps
  • When storing in a DB, encrypt (AES-256, etc.)
  • Store per user; do not mix with other users' tokens
  • Do not output to logs

Related guides

Published: 2026-04-27 Updated: 2026-07-05