Skip to main content

Why tokens expire

The id_token returned by /auth and /get-card expires after approximately 1 hour (3600 seconds). After expiry, authenticated endpoints like /get-card-data will return 401 Unauthorized. The refresh_token is long-lived and can be used to get a new id_token without paying again.

Refreshing a token

Call POST /auth with grant_type: "refresh_token" and your refresh_token. This follows the standard OAuth2 pattern and is free — no x402 payment required.
curl -X POST https://laso.finance/auth \
  -H "Content-Type: application/json" \
  -d '{"grant_type": "refresh_token", "refresh_token": "AMf-vBx4N2..."}'
Response:
{
  "id_token": "eyJhbGciOiJSUzI1NiIs...",
  "refresh_token": "AMf-vBx4N2...",
  "expires_in": "3600",
  "user_id": "0xabc..."
}
The response returns a new refresh_token as well. Always store the latest refresh_token from each response.

Best practices

Proactive refresh

Don’t wait for a 401 error. Refresh the token before it expires:
const TOKEN_LIFETIME_MS = 3600 * 1000; // 1 hour
const REFRESH_BUFFER_MS = 5 * 60 * 1000; // 5 minutes before expiry

let tokenIssuedAt = Date.now();

function shouldRefresh() {
  return Date.now() - tokenIssuedAt > TOKEN_LIFETIME_MS - REFRESH_BUFFER_MS;
}

async function getValidToken() {
  if (shouldRefresh()) {
    const { data } = await axios.post("https://laso.finance/auth", {
      grant_type: "refresh_token",
      refresh_token: currentRefreshToken,
    });
    currentIdToken = data.id_token;
    currentRefreshToken = data.refresh_token;
    tokenIssuedAt = Date.now();
  }
  return currentIdToken;
}

Reactive refresh

Alternatively, catch 401 errors and refresh on demand:
async function callWithRefresh(requestFn) {
  try {
    return await requestFn(currentIdToken);
  } catch (error) {
    if (error.response?.status === 401) {
      const { data } = await axios.post("https://laso.finance/auth", {
        grant_type: "refresh_token",
        refresh_token: currentRefreshToken,
      });
      currentIdToken = data.id_token;
      currentRefreshToken = data.refresh_token;
      return await requestFn(currentIdToken);
    }
    throw error;
  }
}

Store both tokens

Always persist both the id_token and refresh_token. If you lose the refresh_token, you’ll need to call GET /auth again (which costs $0.001 USDC).

Error responses

StatusMeaning
200Success — new tokens returned
400Invalid or missing grant_type or refresh_token in request body
401Token is invalid or revoked — must re-authenticate via /auth