API keys overview
Manage your APIs
Although we hope nothing goes awry with API keys, it can happen. This guide will help you identify, diagnose, and resolve problems that could arise when using API keys.
It’s always better to prevent errors than have to deal with them in a moment of crisis. Here’s some tips for long-term API key management.
Here’s a summary of some of the most common errors and the type of errors that can occur.
See below for full explanations and de-bugging assistance.
Symptoms:
Common causes:
Solutions:
# Verify API key format# Should look like: k_live_1234567890abcdef...
# Check header formatcurl -X GET https://your-domain.kinde.com/api/users \ -H "Authorization: Bearer YOUR_API_KEY" # Note: "Bearer " prefix
# Verify key is active# Go to Settings > API Keys and check if key is enabled
Debugging steps:
Symptoms:
Common causes:
Solutions:
// Implement token refresh logicasync function getValidToken(apiKey) { try { const response = await fetch("https://your-domain.kinde.com/oauth2/token", { method: "POST", headers: { "Content-Type": "application/x-www-form-urlencoded", Authorization: `Bearer ${apiKey}` }, body: "grant_type=client_credentials&scope=openid profile" });
const data = await response.json(); return data.access_token; } catch (error) { throw new Error("Failed to obtain access token"); }}
// Use in your applicationlet accessToken = await getValidToken(apiKey);let tokenExpiry = Date.now() + 3600000; // 1 hour
// Check if token needs refreshif (Date.now() >= tokenExpiry) { accessToken = await getValidToken(apiKey); tokenExpiry = Date.now() + 3600000;}
Debugging steps:
Symptoms:
Common causes:
Solutions:
// Ensure proper header formatconst headers = { Authorization: `Bearer ${apiKey}`, "Content-Type": "application/json"};
// For token exchangeconst formData = new URLSearchParams();formData.append("grant_type", "client_credentials");formData.append("scope", "openid profile");
const response = await fetch("https://your-domain.kinde.com/oauth2/token", { method: "POST", headers: { "Content-Type": "application/x-www-form-urlencoded", Authorization: `Bearer ${apiKey}` }, body: formData});
Symptoms:
Common causes:
Solutions:
# Check current scopes# Go to the relevant user or organization > API Keys > [Your Key] > View# Verify required scopes are assigned
Debugging steps:
Symptoms:
Common causes:
Solutions:
// Verify organization in tokenfunction validateOrganizationAccess(token, requiredOrgCode) { if (!token.org_code) { throw new Error("Organization-scoped token required"); }
if (token.org_code !== requiredOrgCode) { throw new Error("Access denied: organization mismatch"); }
return true;}
// Use in your API endpointapp.get("/api/users/:userId", async (req, res) => { try { const token = extractTokenFromRequest(req); const user = await getUserById(req.params.userId);
// Validate organization access validateOrganizationAccess(token, user.organization_code);
res.json(user); } catch (error) { res.status(403).json({error: error.message}); }});
Debugging steps:
Symptoms:
Common causes:
Solutions:
// Implement rate limiting in your applicationclass RateLimiter { constructor(limit, window) { this.limit = limit; this.window = window; this.requests = new Map(); }
async checkLimit(key) { const now = Date.now(); const windowStart = now - this.window;
// Clean old entries if (this.requests.has(key)) { this.requests.set( key, this.requests.get(key).filter((timestamp) => timestamp > windowStart) ); }
const currentRequests = this.requests.get(key) || [];
if (currentRequests.length >= this.limit) { return false; // Rate limited }
currentRequests.push(now); this.requests.set(key, currentRequests); return true; }}
// Usageconst rateLimiter = new RateLimiter(100, 60000); // 100 requests per minute
if (!(await rateLimiter.checkLimit(apiKey))) { throw new Error("Rate limit exceeded. Please try again later.");}
Debugging steps:
Symptoms:
Common causes:
Solutions:
# Check available scopes# Go to Settings > APIs > Scopes# Verify scope names and availability
# Common standard scopes:# Common standard scopes:# - openid# - profile# - email# - offline_access
**Debugging steps:**
1. Review available scopes in dashboard.2. Check scope spelling and format.3. Verify scope availability for your plan.4. Create custom scopes if needed.