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
| Field | Description |
|---|---|
access_token | The token used to authenticate API requests. Send it as Authorization: Bearer <access_token>. Treat it as a credential. |
expires_in | Remaining 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_type | Always Bearer. Indicates how the token is sent in the Authorization header. |
scope | The 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:
- Request a token and store it together with an expiry timestamp:
now + expires_in. - 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.
- If it is still valid, reuse it. If not, request a new one with the same
client_credentialsrequest.
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:
- On a
401, request a new token and retry the request once. - 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_infrom 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
401exactly once with a fresh token, then fail loudly.