Auth & Identity

Token Expiry and Renewal

Tokens are deliberately short-lived, so well-behaved clients plan for renewal. Handling expiry gracefully keeps integrations reliable while preserving the security benefits of short token lifetimes.

What you'll learn

  • Explain why tokens carry an expiry
  • Describe strategies for renewing access tokens
  • Handle the expired-token error path correctly
  • Avoid common renewal race conditions

6 min

Why tokens expire

An expiry limits how long a token remains usable, and that limit does two useful things at once. It caps the damage if the token ever leaks, since a stolen token soon stops working, and it forces periodic re-validation of access so that revoked permissions take effect within a bounded time. Short-lived access tokens are therefore a deliberate security choice, accepted in exchange for the modest, automatable cost of renewing them.

The practical consequence is that every client calling the Merion API must treat expiry as a completely normal, expected event rather than an exceptional error that catches it off guard. A token lapsing is not a failure of anything; it is the system working as designed. Building that expectation into your client from the start is what separates a robust integration from one that breaks mysteriously every few minutes.

Renewal strategies

There are two broad strategies for renewal, and the best clients often blend them. Reactive renewal waits for a request to fail with an expiry error, then obtains a fresh token and retries the original request. It is simple and requires tracking nothing, but it does mean the occasional request fails first and is retried. Proactive renewal instead refreshes the token shortly before it is due to expire, using its known lifetime, so requests almost never fail for this reason at all.

Proactive renewal yields noticeably smoother behaviour, at the cost of having to track each token's expiry time. Because neither approach is perfect alone, many production clients combine the two: they refresh ahead of time as the norm, while still handling the rare expiry error as a safety net for edge cases such as clock drift or an unexpectedly early expiry.

Handling the error path

When a request does fail because the token has expired, the correct and predictable response is to obtain a fresh token and retry the same request exactly once. The new token may be acquired by presenting a refresh token at the token endpoint, or, where no refresh token exists, by re-running the relevant flow to get a new one.

if (response.status === 401) {
  token = await renewToken();
  response = await retry(request, token);
}

Retrying once, rather than in an unbounded loop, is the important detail: if the second attempt also fails, something other than simple expiry is wrong and the error should surface rather than spin. For the distinction between the access and refresh tokens involved in this dance, see Access Tokens vs Refresh Tokens.

Avoiding race conditions

When many requests happen to expire at the same moment — a common situation in a busy client — a naive implementation may fire several renewals simultaneously, each independently noticing the expiry. That wastes effort at best, and with rotating refresh tokens it is actively harmful, because the parallel renewals can invalidate one another and leave the client with no working token at all.

The fix is to guard renewal so that only one refresh is ever in flight at a time, with any other callers awaiting that single result rather than starting their own. This single-flight pattern keeps renewal correct under concurrency and is well worth implementing carefully. Skipping it is a frequent and frustrating source of intermittent, hard-to-reproduce authentication failures that only appear under load.

Key takeaways

  • Expiry caps the damage from a leaked token
  • Proactive renewal refreshes before expiry; reactive waits for failure
  • On an expiry error, renew once and retry the request
  • Serialise concurrent renewals to avoid races and wasted refreshes

FAQ

Should I refresh before or after a token expires?

Proactively refreshing shortly before expiry avoids failed requests. Keep a reactive fallback to catch any token that expires unexpectedly.

What status indicates an expired token?

Typically a 401 Unauthorized. Treat it as a cue to renew and retry once, rather than surfacing an error immediately to the user.

Why do concurrent renewals cause problems?

With rotating refresh tokens, parallel refreshes can invalidate one another. A single-flight guard ensures only one renewal runs while others wait.

Integrate with Merion

Ready to build?

Read the API reference, grab the OpenAPI spec, and ship a resilient integration.