Skip to main content

Refresh Token

Exchanges a refresh token for a new access token and refresh token. Use this to maintain user sessions without requiring re-login.

Endpoint​

POST /api/v1/auth/refresh

Authentication​

No authentication required, but requires valid refresh token.

Request​

Content-Type​

application/json

Request Body​

FieldTypeRequiredDescription
refresh_tokenstringYesValid refresh token from login or previous refresh

Example Request​

{
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}

Response​

Success Response (200 OK)​

{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "bearer"
}
New Tokens for Both

This endpoint returns both a new access token and a new refresh token. Always replace both tokens in your storage.

Response Fields​

FieldTypeDescription
access_tokenstringNew JWT access token (expires in 30 minutes)
refresh_tokenstringNew JWT refresh token (expires in 7 days)
token_typestringAlways "bearer"

Token Rotation​

This endpoint implements refresh token rotation for security:

  • Each refresh generates a NEW refresh token
  • Old refresh token is invalidated
  • Always update your stored refresh token

Examples​

curl -X POST https://api.callcov.com/api/v1/auth/refresh \
-H "Content-Type: application/json" \
-d '{
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}'

Auto-Refresh Pattern​

Python Client with Auto-Refresh​

import requests
from datetime import datetime, timedelta
import threading

class CallCovClient:
def __init__(self, access_token, refresh_token):
self.base_url = "https://api.callcov.com/api/v1"
self.access_token = access_token
self.refresh_token = refresh_token
self.token_expiry = datetime.now() + timedelta(minutes=30)
self.refresh_lock = threading.Lock()

def refresh_tokens(self):
"""Refresh access and refresh tokens"""
with self.refresh_lock:
response = requests.post(
f"{self.base_url}/auth/refresh",
json={"refresh_token": self.refresh_token}
)

if response.status_code == 200:
data = response.json()
self.access_token = data['access_token']
self.refresh_token = data['refresh_token']
self.token_expiry = datetime.now() + timedelta(minutes=30)
return True
return False

def get_headers(self):
"""Get authorization headers, refreshing token if needed"""
# Refresh if token expires in less than 5 minutes
if datetime.now() + timedelta(minutes=5) >= self.token_expiry:
if not self.refresh_tokens():
raise Exception("Failed to refresh token. Please log in again.")

return {"Authorization": f"Bearer {self.access_token}"}

def make_request(self, method, endpoint, **kwargs):
"""Make authenticated request with auto-refresh"""
headers = self.get_headers()
if 'headers' in kwargs:
kwargs['headers'].update(headers)
else:
kwargs['headers'] = headers

response = requests.request(
method,
f"{self.base_url}{endpoint}",
**kwargs
)

# If 401, try refreshing token once and retry
if response.status_code == 401:
if self.refresh_tokens():
headers = self.get_headers()
kwargs['headers'] = headers
response = requests.request(
method,
f"{self.base_url}{endpoint}",
**kwargs
)

return response

# Usage
client = CallCovClient(access_token, refresh_token)

# Automatically refreshes tokens when needed
response = client.make_request('GET', '/users/me')
print(response.json())

JavaScript/React with Auto-Refresh​

class ApiClient {
constructor(accessToken, refreshToken) {
this.baseUrl = 'https://api.callcov.com/api/v1';
this.accessToken = accessToken;
this.refreshToken = refreshToken;
this.tokenExpiry = Date.now() + 30 * 60 * 1000; // 30 minutes
this.refreshPromise = null;
}

async refreshTokens() {
// Prevent multiple simultaneous refreshes
if (this.refreshPromise) {
return this.refreshPromise;
}

this.refreshPromise = fetch(`${this.baseUrl}/auth/refresh`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ refresh_token: this.refreshToken })
})
.then(async (response) => {
if (!response.ok) throw new Error('Refresh failed');

const data = await response.json();
this.accessToken = data.access_token;
this.refreshToken = data.refresh_token;
this.tokenExpiry = Date.now() + 30 * 60 * 1000;

// Update stored tokens
localStorage.setItem('access_token', data.access_token);
localStorage.setItem('refresh_token', data.refresh_token);

return true;
})
.catch(() => {
// Refresh failed - redirect to login
window.location.href = '/login';
return false;
})
.finally(() => {
this.refreshPromise = null;
});

return this.refreshPromise;
}

async getHeaders() {
// Refresh if token expires in less than 5 minutes
if (Date.now() + 5 * 60 * 1000 >= this.tokenExpiry) {
await this.refreshTokens();
}

return {
'Authorization': `Bearer ${this.accessToken}`
};
}

async request(method, endpoint, options = {}) {
const headers = await this.getHeaders();

const response = await fetch(`${this.baseUrl}${endpoint}`, {
method,
headers: { ...headers, ...options.headers },
...options
});

// If 401, try refreshing once and retry
if (response.status === 401) {
const refreshed = await this.refreshTokens();
if (refreshed) {
const newHeaders = await this.getHeaders();
return fetch(`${this.baseUrl}${endpoint}`, {
method,
headers: { ...newHeaders, ...options.headers },
...options
});
}
}

return response;
}
}

// Usage
const client = new ApiClient(accessToken, refreshToken);
const response = await client.request('GET', '/users/me');
const userData = await response.json();

Errors​

401 Unauthorized​

Invalid refresh token:

{
"detail": "Invalid refresh token"
}

User not found or inactive:

{
"detail": "User not found or inactive"
}

When to Refresh​

Refresh the access token when:

  • Access token has expired (API returns 401)
  • Access token will expire soon (preemptive refresh)
  • Application starts up (check if token is expired)

Best Practices​

  1. Preemptive Refresh: Refresh 5 minutes before expiration
  2. Handle Failures: If refresh fails, redirect to login
  3. Update Storage: Always update both tokens
  4. Thread Safety: Use locks to prevent concurrent refresh attempts
  5. Retry Once: If API returns 401, try refreshing and retrying once

Security Considerations​

  • Refresh token rotation: Each refresh invalidates the old refresh token
  • Expiration: Refresh tokens expire in 7 days
  • Secure storage: Store refresh tokens securely (httpOnly cookies recommended)
  • No reuse: Old refresh tokens cannot be reused

Refresh Token Lifespan​

EventLifespan
Token created7 days
Token used for refreshImmediately invalidated, new one issued
Token expiredCannot be refreshed, user must log in