Skip to main content

Access Tokens

Access tokens are short-lived. Your integration is expected to cache the token it holds, reuse it until shortly before it expires, and then request a new one.

There is exactly one kind of token in this flow: the access token.


No refresh tokens​

The Client Credentials Flow does not issue refresh tokens, and there is no grant_type=refresh_token for our APIs. This is not a restriction of your account — it is how the flow is defined: a refresh token exists to avoid asking a user to log in again, and no user is involved here. RFC 6749, Section 4.4.3 states that a refresh token SHOULD NOT be included in a client credentials response.

To renew, repeat the original token request. Requesting a fresh token needs nothing but your client_id and client_secret — no prior state, and nothing that can be lost between renewals.

If your OAuth library expects a refresh token, configure it for the client credentials grant — most libraries then re-authenticate automatically instead of refreshing.


Token response​

A successful token request returns:

{
"access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI...",
"expires_in": 300,
"token_type": "Bearer",
"scope": "dealer-api"
}

Field descriptions​

FieldDescription
access_tokenThe token used to authenticate API requests. Send it as Authorization: Bearer <access_token>. Treat it as a credential.
expires_inRemaining lifetime of the access token in seconds, counted from the moment the response was issued. Read this value at runtime — never hardcode a lifetime.
token_typeAlways Bearer. Indicates how the token is sent in the Authorization header.
scopeThe scope granted to this token. It determines which API accepts it.

The response contains no refresh_token. The values above are an example — in particular, do not treat the expires_in value shown here as a guaranteed lifetime.


Caching and renewal​

Requesting a new token for every API call is wasteful, slows your integration down and may be rate-limited. Cache the token instead.

A robust pattern:

  1. Request a token and store it together with an expiry timestamp: now + expires_in.
  2. Before each API call, check whether the token is still valid — subtract a safety margin of about 30 seconds to account for clock skew and request duration.
  3. If it is still valid, reuse it. If not, request a new one with the same client_credentials request.
if (now >= expires_at - 30s) {
token = requestNewToken()
}
callApi(token)

Do not build renewal on a fixed schedule derived from a hardcoded lifetime — always drive it from the expires_in value of the token you actually received. Token lifetimes are an operational parameter and can change without a breaking-change announcement.

If your integration runs multiple processes or workers, share the cached token between them where you can, rather than having every worker request its own.


Handling 401 responses​

Even with correct caching, a token can be rejected — for example if it expired between your check and the API receiving the request.

Handle it like this:

  1. On a 401, request a new token and retry the request once.
  2. If the retry also returns 401, stop and surface the error.

Never retry in an unbounded loop: a 401 caused by wrong credentials or a missing scope will not resolve itself, and repeated token requests may be rate-limited.

A 403 is different — the token was accepted, but it is not authorized for that operation. Requesting a new token will not help; check your scope and the permissions agreed during onboarding.


Best practices​

  • Read expires_in from every token response and drive renewal from it.
  • Cache tokens for their lifetime; renew shortly before expiry with a small safety margin.
  • Keep tokens in memory where possible. If you must persist them, use the same protection you use for the client_secret.
  • Never log tokens and never put them in URLs or query parameters.
  • Never expose tokens to a browser or mobile client. Both the token request and the API call belong on your server.
  • Request one token per scope if you integrate with more than one JobRad® API.
  • Retry a 401 exactly once with a fresh token, then fail loudly.