Troubleshooting Guide
This guide covers the most common issues encountered during Freeq API integration and their solutions.
Authentication Issues
401 Unauthorized Error
This is the most common issue during setup. A 401 error indicates a problem with JWT validation.
Systematic Debugging Approach
Step 1: Verify OIDC Discovery Document
curl https://your-domain.com/.well-known/openid-configuration
Expected response:
- 200 OK status
- Valid JSON with required fields
- Publicly accessible (no authentication required)
Common issues:
- Document not publicly accessible
- Missing required fields (
jwks_uri,issuer) - HTTPS certificate issues
Step 2: Check JWKS Endpoint
curl https://your-domain.com/.well-known/jwks.json
Expected response:
{
"keys": [
{
"kid": "key-id",
"kty": "RSA",
"use": "sig",
"alg": "RS256",
"n": "...",
"e": "AQAB"
}
]
}
Common issues:
keysis not an array (most frequent error)- Missing required key properties
- Incorrect key format
- Keys don't match your signing key
Step 3: Validate JWT Claims
Use jwt.io to decode your JWT and verify:
Required claims:
{
"iss": "https://your-domain.com", // Must match registered domain
"aud": "api.freeq.com", // Must be exactly this value
"exp": 1642291200, // Valid expiration timestamp
"email": "user@example.com" // User's email address
}
Common issues:
audclaim incorrect or missingemailclaim missingissdoesn't match OIDC discovery document- Token expired (
expin the past)
Quick Fixes for 401 Errors
-
JWKS Format Error
// Wrong - keys is not an array
{
"keys": {
"kid": "key-id",
"kty": "RSA"
}
}
// Correct - keys must be an array
{
"keys": [
{
"kid": "key-id",
"kty": "RSA",
"n": "...",
"e": "AQAB"
}
]
} -
Missing Audience Claim
// Add this to your JWT payload
{
"aud": "api.freeq.com"
} -
Email Claim Missing
// Ensure email is included
{
"email": "user@example.com"
}
400 Bad Request Error
Common Causes
Missing Project Key
// Request body must include your project key
{
"key": "YOUR_PROJECT_KEY_HERE"
}
Malformed JSON
- Check for trailing commas
- Verify proper JSON syntax
- Ensure Content-Type header is set to
application/json
Invalid Project Key
- Verify you're using the correct key for your environment (testnet vs production)
- Ensure no extra whitespace or characters in the key
- Check that the key matches what was provided by the Freeq team
Webhook Issues
Webhooks Not Being Received
Debugging Steps
Step 1: Verify Endpoint Accessibility
# Test your webhook endpoint externally
curl -X POST https://your-domain.com/webhooks/freeq \
-H 'Content-Type: application/json' \
-d '{"test": "webhook"}'
Step 2: Check Response Codes
Your webhook endpoint must return a 2xx status code:
app.post('/webhooks/freeq', (req, res) => {
try {
// Process webhook
processWebhook(req.body);
// Always return 2xx status
res.status(200).json({ received: true });
} catch (error) {
console.error('Webhook error:', error);
// Even on error, consider returning 2xx if you want to prevent retries
res.status(200).json({ error: error.message });
}
});
Step 3: Enable Webhook Logging
app.post('/webhooks/freeq', (req, res) => {
console.log('Webhook received:', {
timestamp: new Date().toISOString(),
headers: req.headers,
body: req.body
});
// Your processing logic here
res.status(200).json({ received: true });
});
Common Webhook Issues
HTTPS Required in Production
- Webhook endpoints must use HTTPS in production
- Use tools like Let's Encrypt for free SSL certificates
- Test with ngrok for local development
Timeout Issues
// Set reasonable timeouts for webhook processing
app.use(timeout('10s'));
app.post('/webhooks/freeq', (req, res) => {
// Process quickly and acknowledge
res.status(200).json({ received: true });
// Do heavy processing asynchronously
setImmediate(() => {
processWebhookAsync(req.body);
});
});
URL Misconfiguration
- Verify the exact URL provided to Freeq team
- Check for trailing slashes, incorrect paths
- Test the URL manually before providing it
Webhook Retry Logic
If webhooks are failing, they'll be retried when the user next calls /signin:
// Trigger webhook retry by calling signin
async function retryFailedWebhook(userJwt, projectKey) {
try {
const response = await axios.post(
'https://api.testnet.superfreeq.com/api/v1/signin',
{ key: projectKey },
{
headers: {
'Authorization': `Bearer ${userJwt}`,
'Content-Type': 'application/json',
},
}
);
console.log('Signin call completed, webhook retry triggered if needed');
return response.data;
} catch (error) {
console.error('Failed to trigger webhook retry:', error);
throw error;
}
}
Integration Flow Issues
Wallet Creation Not Working
For Async Flow
Issue: Called /createasync but no wallet created
Debugging:
- Check if you received a
202 Acceptedresponse - Verify webhook endpoint is working
- Try calling
/signinto trigger retry - Check server logs for any processing errors
Solution:
// Robust async wallet creation
async function createWalletAsync(userJwt, projectKey, userId) {
try {
const response = await axios.post(
'https://api.testnet.superfreeq.com/api/v1/wallets/createasync',
{
key: projectKey,
token: `user-${userId}` // Help identify user in webhook
},
{
headers: {
'Authorization': `Bearer ${userJwt}`,
'Content-Type': 'application/json',
},
timeout: 15000, // 15 second timeout
}
);
if (response.status === 202) {
console.log(`Wallet creation requested for user ${userId}`);
return { status: 'pending', message: 'Wallet creation in progress' };
}
} catch (error) {
console.error('Async wallet creation failed:', error);
throw error;
}
}
For Sync Flow
Issue: /signin not creating wallet for new users
Debugging:
- Verify JWT claims include email
- Check if user actually needs a new wallet
- Review response for error details
Solution:
// Sync wallet creation/retrieval
async function getOrCreateWallet(userJwt, projectKey) {
try {
const response = await axios.post(
'https://api.testnet.superfreeq.com/api/v1/signin',
{ key: projectKey },
{
headers: {
'Authorization': `Bearer ${userJwt}`,
'Content-Type': 'application/json',
},
timeout: 30000, // Longer timeout for wallet creation
}
);
return response.data;
} catch (error) {
console.error('Wallet creation/retrieval failed:', error);
throw error;
}
}
Duplicate Wallet Issues
Issue: Concerned about creating duplicate wallets
Solution: The system is idempotent based on email address. Multiple calls for the same user won't create duplicate wallets.
// Safe to call multiple times for same user
await createWalletAsync(userJwt, projectKey, userId);
await createWalletAsync(userJwt, projectKey, userId); // Won't create duplicate
Development Environment Issues
Local Testing Problems
Webhook Testing with ngrok
# Install ngrok
npm install -g ngrok
# Expose local port
ngrok http 3000
# Use the HTTPS URL for webhook configuration
# Example: https://abc123.ngrok.io/webhooks/freeq
Environment Variables
# .env file for development
FREEQ_PROJECT_KEY=your_dev_project_key
FREEQ_API_URL=https://api.testnet.superfreeq.com
WEBHOOK_SECRET=your_webhook_secret
# Load in your application
require('dotenv').config();
CORS Issues in Development
// If calling from frontend (not recommended for production)
const cors = require('cors');
app.use(cors({
origin: ['http://localhost:3000', 'https://your-domain.com'],
credentials: true
}));
JWT Testing Issues
Generate Test JWTs
const jwt = require('jsonwebtoken');
const fs = require('fs');
// Read your private key
const privateKey = fs.readFileSync('path/to/your/private-key.pem');
// Generate test JWT
const testJWT = jwt.sign(
{
iss: 'https://your-domain.com',
aud: 'api.freeq.com',
exp: Math.floor(Date.now() / 1000) + (60 * 60), // 1 hour
email: 'test@example.com',
sub: 'test-user-123'
},
privateKey,
{ algorithm: 'RS256', keyid: 'your-key-id' }
);
console.log('Test JWT:', testJWT);
Validate JWT Locally
const jwt = require('jsonwebtoken');
const jwksClient = require('jwks-rsa');
const client = jwksClient({
jwksUri: 'https://your-domain.com/.well-known/jwks.json'
});
function getKey(header, callback) {
client.getSigningKey(header.kid, (err, key) => {
const signingKey = key.publicKey || key.rsaPublicKey;
callback(null, signingKey);
});
}
// Verify JWT
jwt.verify(token, getKey, {
audience: 'api.freeq.com',
issuer: 'https://your-domain.com',
algorithms: ['RS256']
}, (err, decoded) => {
if (err) {
console.error('JWT validation failed:', err);
} else {
console.log('JWT valid:', decoded);
}
});
Production Issues
Performance Optimization
Connection Pooling
// Use HTTP agent for connection reuse
const https = require('https');
const axios = require('axios');
const agent = new https.Agent({
keepAlive: true,
maxSockets: 10
});
const freeqAPI = axios.create({
baseURL: 'https://api.testnet.superfreeq.com',
httpsAgent: agent,
timeout: 10000
});
Rate Limiting
// Implement rate limiting for API calls
const rateLimit = require('express-rate-limit');
const apiLimiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100 // limit each IP to 100 requests per windowMs
});
app.use('/api/', apiLimiter);
Error Monitoring
Logging Best Practices
const winston = require('winston');
const logger = winston.createLogger({
level: 'info',
format: winston.format.combine(
winston.format.timestamp(),
winston.format.errors({ stack: true }),
winston.format.json()
),
transports: [
new winston.transports.File({ filename: 'error.log', level: 'error' }),
new winston.transports.File({ filename: 'combined.log' })
]
});
// Log Freeq API calls
async function loggedFreeqCall(endpoint, data, userJwt) {
const startTime = Date.now();
try {
const response = await freeqAPI.post(endpoint, data, {
headers: { 'Authorization': `Bearer ${userJwt}` }
});
logger.info('Freeq API success', {
endpoint,
duration: Date.now() - startTime,
status: response.status
});
return response.data;
} catch (error) {
logger.error('Freeq API error', {
endpoint,
duration: Date.now() - startTime,
error: error.message,
status: error.response?.status,
data: error.response?.data
});
throw error;
}
}
Frequently Asked Questions
What happens if I call /createasync for a user who already has a wallet?
The system is idempotent based on the user's email address. If a wallet already exists, the system acknowledges the request without creating a duplicate. No error is returned.
Can I test the API without implementing full JWT authentication?
For initial testing, you can use the interactive Swagger documentation which handles authentication through Auth0. However, for your application integration, you must implement proper JWT authentication.
How long does wallet creation take?
Asynchronous wallet creation typically takes 30 seconds to 2 minutes. Synchronous creation (via /signin) usually completes within 10-30 seconds but may take longer during high load periods.
What should I do if webhooks are consistently failing?
- Check your webhook endpoint logs for errors
- Verify you're returning 2xx status codes
- Test the endpoint manually with curl
- Contact Freeq support with your webhook URL and error details
Can I change my project key after integration?
Project keys can be rotated, but this requires coordination with the Freeq team. Contact support to schedule key rotation to avoid service interruption.
Getting Additional Help
Before Contacting Support
- Complete the prerequisites checklist
- Test each component individually (OIDC, JWKS, JWT)
- Check your application logs for specific error messages
- Try the solutions in this troubleshooting guide
Information to Include When Requesting Help
- Error messages (exact text and HTTP status codes)
- Your issuer domain and OIDC discovery document URL
- Sample JWT (with sensitive data redacted)
- Webhook endpoint URL (if using webhooks)
- Steps you've already tried from this troubleshooting guide
Contact Information
- Technical Support: support@freeq.com
- Discord Community: discord.gg/freeq
- Documentation Issues: Report problems with this documentation via GitHub
Additional Resources
- Interactive API Testing - Swagger documentation
- JWT Decoder - Debug and validate JWTs
- OIDC Debugger - Test OpenID Connect flows
- ngrok - Expose local endpoints for webhook testing
If you've worked through this troubleshooting guide and are still experiencing problems, don't hesitate to contact the Freeq team. We're here to help ensure your integration is successful.